Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
flutter_windows_engine.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
6
7#include <dwmapi.h>
8
9#include <filesystem>
10#include <shared_mutex>
11#include <sstream>
12
13#include "flutter/fml/logging.h"
14#include "flutter/fml/paths.h"
32
33// winbase.h defines GetCurrentTime as a macro.
34#undef GetCurrentTime
35
36static constexpr char kAccessibilityChannelName[] = "flutter/accessibility";
37
38namespace flutter {
39
40namespace {
41
42// Lifted from vsync_waiter_fallback.cc
43static std::chrono::nanoseconds SnapToNextTick(
44 std::chrono::nanoseconds value,
45 std::chrono::nanoseconds tick_phase,
46 std::chrono::nanoseconds tick_interval) {
47 std::chrono::nanoseconds offset = (tick_phase - value) % tick_interval;
48 if (offset != std::chrono::nanoseconds::zero())
49 offset = offset + tick_interval;
50 return value + offset;
51}
52
53// Creates and returns a FlutterRendererConfig that renders to the view (if any)
54// of a FlutterWindowsEngine, using OpenGL (via ANGLE).
55// The user_data received by the render callbacks refers to the
56// FlutterWindowsEngine.
57FlutterRendererConfig GetOpenGLRendererConfig() {
58 FlutterRendererConfig config = {};
59 config.type = kOpenGL;
60 config.open_gl.struct_size = sizeof(config.open_gl);
61 config.open_gl.make_current = [](void* user_data) -> bool {
62 auto host = static_cast<FlutterWindowsEngine*>(user_data);
63 if (!host->egl_manager()) {
64 return false;
65 }
66 return host->egl_manager()->render_context()->MakeCurrent();
67 };
68 config.open_gl.clear_current = [](void* user_data) -> bool {
69 auto host = static_cast<FlutterWindowsEngine*>(user_data);
70 if (!host->egl_manager()) {
71 return false;
72 }
73 return host->egl_manager()->render_context()->ClearCurrent();
74 };
75 config.open_gl.present = [](void* user_data) -> bool { FML_UNREACHABLE(); };
76 config.open_gl.fbo_reset_after_present = true;
78 [](void* user_data, const FlutterFrameInfo* info) -> uint32_t {
80 };
81 config.open_gl.gl_proc_resolver = [](void* user_data,
82 const char* what) -> void* {
83 return reinterpret_cast<void*>(eglGetProcAddress(what));
84 };
85 config.open_gl.make_resource_current = [](void* user_data) -> bool {
86 auto host = static_cast<FlutterWindowsEngine*>(user_data);
87 if (!host->egl_manager()) {
88 return false;
89 }
90 return host->egl_manager()->resource_context()->MakeCurrent();
91 };
93 [](void* user_data, int64_t texture_id, size_t width, size_t height,
95 auto host = static_cast<FlutterWindowsEngine*>(user_data);
96 if (!host->texture_registrar()) {
97 return false;
98 }
99 return host->texture_registrar()->PopulateTexture(texture_id, width, height,
100 texture);
101 };
102 return config;
103}
104
105// Creates and returns a FlutterRendererConfig that renders to the view (if any)
106// of a FlutterWindowsEngine, using software rasterization.
107// The user_data received by the render callbacks refers to the
108// FlutterWindowsEngine.
109FlutterRendererConfig GetSoftwareRendererConfig() {
110 FlutterRendererConfig config = {};
111 config.type = kSoftware;
112 config.software.struct_size = sizeof(config.software);
114 [](void* user_data, const void* allocation, size_t row_bytes,
115 size_t height) {
117 return false;
118 };
119 return config;
120}
121
122// Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
124 const FlutterPlatformMessage& engine_message) {
126 message.struct_size = sizeof(message);
127 message.channel = engine_message.channel;
128 message.message = engine_message.message;
129 message.message_size = engine_message.message_size;
130 message.response_handle = engine_message.response_handle;
131 return message;
132}
133
134// Converts a LanguageInfo struct to a FlutterLocale struct. |info| must outlive
135// the returned value, since the returned FlutterLocale has pointers into it.
136FlutterLocale CovertToFlutterLocale(const LanguageInfo& info) {
137 FlutterLocale locale = {};
138 locale.struct_size = sizeof(FlutterLocale);
139 locale.language_code = info.language.c_str();
140 if (!info.region.empty()) {
141 locale.country_code = info.region.c_str();
142 }
143 if (!info.script.empty()) {
144 locale.script_code = info.script.c_str();
145 }
146 return locale;
147}
148
149} // namespace
150
152 const FlutterProjectBundle& project,
153 std::shared_ptr<WindowsProcTable> windows_proc_table)
154 : project_(std::make_unique<FlutterProjectBundle>(project)),
155 windows_proc_table_(std::move(windows_proc_table)),
156 aot_data_(nullptr, nullptr),
157 lifecycle_manager_(std::make_unique<WindowsLifecycleManager>(this)) {
158 if (windows_proc_table_ == nullptr) {
159 windows_proc_table_ = std::make_shared<WindowsProcTable>();
160 }
161
163
164 embedder_api_.struct_size = sizeof(FlutterEngineProcTable);
165 FlutterEngineGetProcAddresses(&embedder_api_);
166
167 task_runner_ =
168 std::make_unique<TaskRunner>(
169 embedder_api_.GetCurrentTime, [this](const auto* task) {
170 if (!engine_) {
171 FML_LOG(ERROR)
172 << "Cannot post an engine task when engine is not running.";
173 return;
174 }
175 if (embedder_api_.RunTask(engine_, task) != kSuccess) {
176 FML_LOG(ERROR) << "Failed to post an engine task.";
177 }
178 });
179
180 // Set up the legacy structs backing the API handles.
181 messenger_ =
183 messenger_->SetEngine(this);
184 plugin_registrar_ = std::make_unique<FlutterDesktopPluginRegistrar>();
185 plugin_registrar_->engine = this;
186
187 messenger_wrapper_ =
188 std::make_unique<BinaryMessengerImpl>(messenger_->ToRef());
189 message_dispatcher_ =
190 std::make_unique<IncomingMessageDispatcher>(messenger_->ToRef());
191
192 texture_registrar_ =
193 std::make_unique<FlutterWindowsTextureRegistrar>(this, gl_);
194
195 // Check for impeller support.
196 auto& switches = project_->GetSwitches();
197 enable_impeller_ = std::find(switches.begin(), switches.end(),
198 "--enable-impeller=true") != switches.end();
199
200 egl_manager_ = egl::Manager::Create(
201 static_cast<egl::GpuPreference>(project_->gpu_preference()));
202 window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
203
204 display_manager_ = std::make_shared<DisplayManagerWin32>(this);
205
206 window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
207 [](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,
208 LRESULT* result) {
211 static_cast<FlutterWindowsEngine*>(user_data);
212
213 BASE_DCHECK(that->display_manager_);
214 if (that->display_manager_->HandleWindowMessage(hwnd, msg, wpar, lpar,
215 result)) {
216 return true;
217 }
218
219 BASE_DCHECK(that->lifecycle_manager_);
220 bool handled =
221 that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar, result);
222 if (handled) {
223 return true;
224 }
225 auto message_result =
226 that->window_manager_->HandleMessage(hwnd, msg, wpar, lpar);
227 if (message_result) {
228 *result = *message_result;
229 return true;
230 }
231 return false;
232 },
233 static_cast<void*>(this));
234
235 // Set up internal channels.
236 // TODO: Replace this with an embedder.h API. See
237 // https://github.com/flutter/flutter/issues/71099
238 internal_plugin_registrar_ =
239 std::make_unique<PluginRegistrar>(plugin_registrar_.get());
240
241 accessibility_plugin_ = std::make_unique<AccessibilityPlugin>(this);
242 AccessibilityPlugin::SetUp(messenger_wrapper_.get(),
243 accessibility_plugin_.get());
244
245 cursor_handler_ =
246 std::make_unique<CursorHandler>(messenger_wrapper_.get(), this);
247 platform_handler_ =
248 std::make_unique<PlatformHandler>(messenger_wrapper_.get(), this);
249 window_manager_ = std::make_unique<WindowManager>(this);
250 settings_plugin_ = std::make_unique<SettingsPlugin>(messenger_wrapper_.get(),
251 task_runner_.get());
252}
253
254FlutterWindowsEngine::~FlutterWindowsEngine() {
255 messenger_->SetEngine(nullptr);
256 Stop();
257}
258
259FlutterWindowsEngine* FlutterWindowsEngine::GetEngineForId(int64_t engine_id) {
260 return reinterpret_cast<FlutterWindowsEngine*>(engine_id);
261}
262
263void FlutterWindowsEngine::SetSwitches(
264 const std::vector<std::string>& switches) {
265 project_->SetSwitches(switches);
266}
267
268bool FlutterWindowsEngine::Run() {
269 return Run("");
270}
271
272bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
273 if (!project_->HasValidPaths()) {
274 FML_LOG(ERROR) << "Missing or unresolvable paths to assets.";
275 return false;
276 }
277 std::string assets_path_string = fml::PathToUtf8(project_->assets_path());
278 std::string icu_path_string = fml::PathToUtf8(project_->icu_path());
279 if (embedder_api_.RunsAOTCompiledDartCode()) {
280 aot_data_ = project_->LoadAotData(embedder_api_);
281 if (!aot_data_) {
282 FML_LOG(ERROR) << "Unable to start engine without AOT data.";
283 return false;
284 }
285 }
286
287 // FlutterProjectArgs is expecting a full argv, so when processing it for
288 // flags the first item is treated as the executable and ignored. Add a dummy
289 // value so that all provided arguments are used.
290 std::string executable_name = GetExecutableName();
291 std::vector<const char*> argv = {executable_name.c_str()};
292 std::vector<std::string> switches = project_->GetSwitches();
293 if (enable_impeller_) {
294 if (std::find(switches.begin(), switches.end(),
295 "--impeller-use-sdfs=true") == switches.end() &&
296 std::find(switches.begin(), switches.end(),
297 "--impeller-use-sdfs=false") == switches.end()) {
298 switches.push_back("--impeller-use-sdfs=true");
299 }
300 }
301 std::transform(
302 switches.begin(), switches.end(), std::back_inserter(argv),
303 [](const std::string& arg) -> const char* { return arg.c_str(); });
304
305 const std::vector<std::string>& entrypoint_args =
306 project_->dart_entrypoint_arguments();
307 std::vector<const char*> entrypoint_argv;
308 std::transform(
309 entrypoint_args.begin(), entrypoint_args.end(),
310 std::back_inserter(entrypoint_argv),
311 [](const std::string& arg) -> const char* { return arg.c_str(); });
312
313 // Configure task runners.
314 FlutterTaskRunnerDescription platform_task_runner = {};
315 platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
316 platform_task_runner.user_data = task_runner_.get();
317 platform_task_runner.runs_task_on_current_thread_callback =
318 [](void* user_data) -> bool {
319 return static_cast<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
320 };
321 platform_task_runner.post_task_callback = [](FlutterTask task,
322 uint64_t target_time_nanos,
323 void* user_data) -> void {
324 static_cast<TaskRunner*>(user_data)->PostFlutterTask(task,
325 target_time_nanos);
326 };
327 FlutterCustomTaskRunners custom_task_runners = {};
328 custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
329 custom_task_runners.platform_task_runner = &platform_task_runner;
330 custom_task_runners.thread_priority_setter =
331 &WindowsPlatformThreadPrioritySetter;
332
333 if (project_->ui_thread_policy() !=
334 FlutterUIThreadPolicy::RunOnSeparateThread) {
335 custom_task_runners.ui_task_runner = &platform_task_runner;
336 } else {
337 FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This "
338 "will be removed in future.";
339 }
340
343 args.shutdown_dart_vm_when_done = true;
344 args.assets_path = assets_path_string.c_str();
345 args.icu_data_path = icu_path_string.c_str();
346 args.command_line_argc = static_cast<int>(argv.size());
347 args.command_line_argv = argv.empty() ? nullptr : argv.data();
348 args.engine_id = reinterpret_cast<int64_t>(this);
349
350 // Fail if conflicting non-default entrypoints are specified in the method
351 // argument and the project.
352 //
353 // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
354 // The entrypoint method parameter should eventually be removed from this
355 // method and only the entrypoint specified in project_ should be used.
356 if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
357 project_->dart_entrypoint() != entrypoint) {
358 FML_LOG(ERROR) << "Conflicting entrypoints were specified in "
359 "FlutterDesktopEngineProperties.dart_entrypoint and "
360 "FlutterDesktopEngineRun(engine, entry_point). ";
361 return false;
362 }
363 if (!entrypoint.empty()) {
364 args.custom_dart_entrypoint = entrypoint.data();
365 } else if (!project_->dart_entrypoint().empty()) {
366 args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
367 }
368 args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
369 args.dart_entrypoint_argv =
370 entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
371 args.platform_message_callback =
372 [](const FlutterPlatformMessage* engine_message,
373 void* user_data) -> void {
374 auto host = static_cast<FlutterWindowsEngine*>(user_data);
375 return host->HandlePlatformMessage(engine_message);
376 };
377 args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
378 auto host = static_cast<FlutterWindowsEngine*>(user_data);
379 host->OnVsync(baton);
380 };
381 args.on_pre_engine_restart_callback = [](void* user_data) {
382 auto host = static_cast<FlutterWindowsEngine*>(user_data);
383 host->OnPreEngineRestart();
384 };
385 args.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
386 void* user_data) {
387 auto host = static_cast<FlutterWindowsEngine*>(user_data);
388
389 auto view = host->view(update->view_id);
390 if (!view) {
391 return;
392 }
393
394 auto accessibility_bridge = view->accessibility_bridge().lock();
395 if (!accessibility_bridge) {
396 return;
397 }
398
399 for (size_t i = 0; i < update->node_count; i++) {
400 const FlutterSemanticsNode2* node = update->nodes[i];
401 accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
402 }
403
404 for (size_t i = 0; i < update->custom_action_count; i++) {
406 accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*action);
407 }
408
409 accessibility_bridge->CommitUpdates();
410 };
411 args.root_isolate_create_callback = [](void* user_data) {
412 auto host = static_cast<FlutterWindowsEngine*>(user_data);
413 if (host->root_isolate_create_callback_) {
414 host->root_isolate_create_callback_();
415 }
416 };
417 args.channel_update_callback = [](const FlutterChannelUpdate* update,
418 void* user_data) {
419 auto host = static_cast<FlutterWindowsEngine*>(user_data);
420 if (SAFE_ACCESS(update, channel, nullptr) != nullptr) {
421 std::string channel_name(update->channel);
422 host->OnChannelUpdate(std::move(channel_name),
423 SAFE_ACCESS(update, listening, false));
424 }
425 };
426 args.view_focus_change_request_callback =
427 [](const FlutterViewFocusChangeRequest* request, void* user_data) {
428 auto host = static_cast<FlutterWindowsEngine*>(user_data);
429 host->OnViewFocusChangeRequest(request);
430 };
431
432 args.custom_task_runners = &custom_task_runners;
433
434 if (!platform_view_plugin_) {
435 platform_view_plugin_ = std::make_unique<PlatformViewPlugin>(
436 messenger_wrapper_.get(), task_runner_.get());
437 }
438 if (egl_manager_) {
439 auto resolver = [](const char* name) -> void* {
440 return reinterpret_cast<void*>(::eglGetProcAddress(name));
441 };
442
443 // TODO(schectman) Pass the platform view manager to the compositor
444 // constructors: https://github.com/flutter/flutter/issues/143375
445 compositor_ =
446 std::make_unique<CompositorOpenGL>(this, resolver, enable_impeller_);
447 } else {
448 compositor_ = std::make_unique<CompositorSoftware>();
449 }
450
451 FlutterCompositor compositor = {};
452 compositor.struct_size = sizeof(FlutterCompositor);
453 compositor.user_data = this;
454 compositor.create_backing_store_callback =
455 [](const FlutterBackingStoreConfig* config,
456 FlutterBackingStore* backing_store_out, void* user_data) -> bool {
457 auto host = static_cast<FlutterWindowsEngine*>(user_data);
458
459 return host->compositor_->CreateBackingStore(*config, backing_store_out);
460 };
461
462 compositor.collect_backing_store_callback =
463 [](const FlutterBackingStore* backing_store, void* user_data) -> bool {
464 auto host = static_cast<FlutterWindowsEngine*>(user_data);
465
466 return host->compositor_->CollectBackingStore(backing_store);
467 };
468
469 compositor.present_view_callback =
470 [](const FlutterPresentViewInfo* info) -> bool {
471 auto host = static_cast<FlutterWindowsEngine*>(info->user_data);
472
473 return host->Present(info);
474 };
475 args.compositor = &compositor;
476
477 if (aot_data_) {
478 args.aot_data = aot_data_.get();
479 }
480
481 // The platform thread creates OpenGL contexts. These
482 // must be released to be used by the engine's threads.
483 FML_DCHECK(!egl_manager_ || !egl_manager_->HasContextCurrent());
484
485 FlutterRendererConfig renderer_config;
486
487 if (enable_impeller_) {
488 // Impeller does not support a Software backend. Avoid falling back and
489 // confusing the engine on which renderer is selected.
490 if (!egl_manager_) {
491 FML_LOG(ERROR) << "Could not create surface manager. Impeller backend "
492 "does not support software rendering.";
493 return false;
494 }
495 renderer_config = GetOpenGLRendererConfig();
496 } else {
497 renderer_config =
498 egl_manager_ ? GetOpenGLRendererConfig() : GetSoftwareRendererConfig();
499 }
500
501 auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
502 &args, this, &engine_);
503 if (result != kSuccess || engine_ == nullptr) {
504 FML_LOG(ERROR) << "Failed to start Flutter engine: error " << result;
505 return false;
506 }
507
508 display_manager_->UpdateDisplays();
509
510 SendSystemLocales();
511
512 settings_plugin_->StartWatching();
513 settings_plugin_->SendSettings();
514
515 InitializeKeyboard();
516
517 return true;
518}
519
520bool FlutterWindowsEngine::Stop() {
521 if (engine_) {
522 window_manager_->OnEngineShutdown();
523 for (const auto& [callback, registrar] :
524 plugin_registrar_destruction_callbacks_) {
525 callback(registrar);
526 }
527 FlutterEngineResult result = embedder_api_.Shutdown(engine_);
528 engine_ = nullptr;
529 return (result == kSuccess);
530 }
531 return false;
532}
533
534std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView(
535 std::unique_ptr<WindowBindingHandler> window,
536 bool is_sized_to_content,
537 const BoxConstraints& box_constraints,
538 FlutterWindowsViewSizingDelegate* sizing_delegate) {
539 auto view_id = next_view_id_;
540 auto view = std::make_unique<FlutterWindowsView>(
541 view_id, this, std::move(window), is_sized_to_content, box_constraints,
542 sizing_delegate, windows_proc_table_);
543
544 view->CreateRenderSurface();
545 view->UpdateSemanticsEnabled(semantics_enabled_);
546
547 next_view_id_++;
548
549 {
550 // Add the view to the embedder. This must happen before the engine
551 // is notified the view exists and starts presenting to it.
552 std::unique_lock write_lock(views_mutex_);
553 FML_DCHECK(views_.find(view_id) == views_.end());
554 views_[view_id] = view.get();
555 }
556
557 if (!view->IsImplicitView()) {
558 FML_DCHECK(running());
559
560 struct Captures {
562 bool added;
563 };
564 Captures captures = {};
565
566 FlutterWindowMetricsEvent metrics = view->CreateWindowMetricsEvent();
567
568 FlutterAddViewInfo info = {};
569 info.struct_size = sizeof(FlutterAddViewInfo);
570 info.view_id = view_id;
571 info.view_metrics = &metrics;
572 info.user_data = &captures;
573 info.add_view_callback = [](const FlutterAddViewResult* result) {
574 Captures* captures = reinterpret_cast<Captures*>(result->user_data);
575 captures->added = result->added;
576 captures->latch.Signal();
577 };
578
579 FlutterEngineResult result = embedder_api_.AddView(engine_, &info);
580 if (result != kSuccess) {
581 FML_LOG(ERROR)
582 << "Starting the add view operation failed. FlutterEngineAddView "
583 "returned an unexpected result: "
584 << result << ". This indicates a bug in the Windows embedder.";
585 FML_DCHECK(false);
586 return nullptr;
587 }
588
589 // Block the platform thread until the engine has added the view.
590 // TODO(loicsharma): This blocks the platform thread eagerly and can
591 // cause unnecessary delay in input processing. Instead, this should block
592 // lazily only when the app does an operation which needs the view.
593 // https://github.com/flutter/flutter/issues/146248
594 captures.latch.Wait();
595
596 if (!captures.added) {
597 // Adding the view failed. Update the embedder's state to match the
598 // engine's state. This is unexpected and indicates a bug in the Windows
599 // embedder.
600 FML_LOG(ERROR) << "FlutterEngineAddView failed to add view";
601 std::unique_lock write_lock(views_mutex_);
602 views_.erase(view_id);
603 return nullptr;
604 }
605 }
606
607 return std::move(view);
608}
609
610void FlutterWindowsEngine::RemoveView(FlutterViewId view_id) {
611 FML_DCHECK(running());
612
613 // Notify the engine to stop rendering to the view if it isn't the implicit
614 // view. The engine and framework assume the implicit view always exists and
615 // can continue presenting.
616 if (view_id != kImplicitViewId) {
617 struct Captures {
619 bool removed;
620 };
621 Captures captures = {};
622
623 FlutterRemoveViewInfo info = {};
624 info.struct_size = sizeof(FlutterRemoveViewInfo);
625 info.view_id = view_id;
626 info.user_data = &captures;
627 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
628 // This is invoked on an engine thread. If
629 // |FlutterRemoveViewResult.removed| is `true`, the engine guarantees the
630 // view won't be presented.
631 Captures* captures = reinterpret_cast<Captures*>(result->user_data);
632 captures->removed = result->removed;
633 captures->latch.Signal();
634 };
635
636 FlutterEngineResult result = embedder_api_.RemoveView(engine_, &info);
637 if (result != kSuccess) {
638 FML_LOG(ERROR) << "Starting the remove view operation failed. "
639 "FlutterEngineRemoveView "
640 "returned an unexpected result: "
641 << result
642 << ". This indicates a bug in the Windows embedder.";
643 FML_DCHECK(false);
644 return;
645 }
646
647 // Block the platform thread until the engine has removed the view.
648 // TODO(loicsharma): This blocks the platform thread eagerly and can
649 // cause unnecessary delay in input processing. Instead, this should block
650 // lazily only when an operation needs the view.
651 // https://github.com/flutter/flutter/issues/146248
652 captures.latch.Wait();
653
654 if (!captures.removed) {
655 // Removing the view failed. This is unexpected and indicates a bug in the
656 // Windows embedder.
657 FML_LOG(ERROR) << "FlutterEngineRemoveView failed to remove view";
658 return;
659 }
660 }
661
662 {
663 // The engine no longer presents to the view. Remove the view from the
664 // embedder.
665 std::unique_lock write_lock(views_mutex_);
666
667 FML_DCHECK(views_.find(view_id) != views_.end());
668
669 // Reset text input state if the removed view is the active text input
670 // view, to prevent stale view references.
671 if (text_input_plugin_) {
672 text_input_plugin_->OnViewRemoved(view_id);
673 }
674
675 views_.erase(view_id);
676 }
677}
678
679void FlutterWindowsEngine::OnVsync(intptr_t baton) {
680 std::chrono::nanoseconds current_time =
681 std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
682 std::chrono::nanoseconds frame_interval = FrameInterval();
683 auto next = SnapToNextTick(current_time, start_time_, frame_interval);
684 embedder_api_.OnVsync(engine_, baton, next.count(),
685 (next + frame_interval).count());
686}
687
688std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
689 if (frame_interval_override_.has_value()) {
690 return frame_interval_override_.value();
691 }
692 uint64_t interval = 16600000;
693
694 DWM_TIMING_INFO timing_info = {};
695 timing_info.cbSize = sizeof(timing_info);
696 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
697 if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
698 timing_info.rateRefresh.uiNumerator > 0) {
699 interval = static_cast<double>(timing_info.rateRefresh.uiDenominator *
700 1000000000.0) /
701 static_cast<double>(timing_info.rateRefresh.uiNumerator);
702 }
703
704 return std::chrono::nanoseconds(interval);
705}
706
707FlutterWindowsView* FlutterWindowsEngine::view(FlutterViewId view_id) const {
708 std::shared_lock read_lock(views_mutex_);
709
710 auto iterator = views_.find(view_id);
711 if (iterator == views_.end()) {
712 return nullptr;
713 }
714
715 return iterator->second;
716}
717
718// Returns the currently configured Plugin Registrar.
719FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
720 return plugin_registrar_.get();
721}
722
723void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
726 plugin_registrar_destruction_callbacks_[callback] = registrar;
727}
728
729void FlutterWindowsEngine::UpdateDisplay(
730 const std::vector<FlutterEngineDisplay>& displays) {
731 if (engine_) {
732 embedder_api_.NotifyDisplayUpdate(engine_,
734 displays.data(), displays.size());
735 }
736}
737
738void FlutterWindowsEngine::SendWindowMetricsEvent(
739 const FlutterWindowMetricsEvent& event) {
740 if (engine_) {
741 embedder_api_.SendWindowMetricsEvent(engine_, &event);
742 }
743}
744
745void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
746 if (engine_) {
747 embedder_api_.SendPointerEvent(engine_, &event, 1);
748 }
749}
750
751void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
753 void* user_data) {
754 if (engine_) {
755 embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
756 }
757}
758
759void FlutterWindowsEngine::SendViewFocusEvent(
760 const FlutterViewFocusEvent& event) {
761 if (engine_) {
762 embedder_api_.SendViewFocusEvent(engine_, &event);
763 }
764}
765
766bool FlutterWindowsEngine::SendPlatformMessage(
767 const char* channel,
768 const uint8_t* message,
769 const size_t message_size,
770 const FlutterDesktopBinaryReply reply,
771 void* user_data) {
772 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
773 if (reply != nullptr && user_data != nullptr) {
774 FlutterEngineResult result =
775 embedder_api_.PlatformMessageCreateResponseHandle(
776 engine_, reply, user_data, &response_handle);
777 if (result != kSuccess) {
778 FML_LOG(ERROR) << "Failed to create response handle";
779 return false;
780 }
781 }
782
783 FlutterPlatformMessage platform_message = {
785 channel,
786 message,
787 message_size,
788 response_handle,
789 };
790
791 FlutterEngineResult message_result =
792 embedder_api_.SendPlatformMessage(engine_, &platform_message);
793 if (response_handle != nullptr) {
794 embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
795 response_handle);
796 }
797 return message_result == kSuccess;
798}
799
800void FlutterWindowsEngine::SendPlatformMessageResponse(
802 const uint8_t* data,
803 size_t data_length) {
804 embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
805}
806
807void FlutterWindowsEngine::HandlePlatformMessage(
808 const FlutterPlatformMessage* engine_message) {
809 if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
810 FML_LOG(ERROR) << "Invalid message size received. Expected: "
811 << sizeof(FlutterPlatformMessage) << " but received "
812 << engine_message->struct_size;
813 return;
814 }
815
816 auto message = ConvertToDesktopMessage(*engine_message);
817
818 message_dispatcher_->HandleMessage(message, [this] {}, [this] {});
819}
820
821void FlutterWindowsEngine::ReloadSystemFonts() {
822 embedder_api_.ReloadSystemFonts(engine_);
823}
824
825void FlutterWindowsEngine::ScheduleFrame() {
826 embedder_api_.ScheduleFrame(engine_);
827}
828
829void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
830 next_frame_callback_ = std::move(callback);
831
832 embedder_api_.SetNextFrameCallback(
833 engine_,
834 [](void* user_data) {
835 // Embedder callback runs on raster thread. Switch back to platform
836 // thread.
838 static_cast<FlutterWindowsEngine*>(user_data);
839
840 self->task_runner_->PostTask(std::move(self->next_frame_callback_));
841 },
842 this);
843}
844
845HCURSOR FlutterWindowsEngine::GetCursorByName(
846 const std::string& cursor_name) const {
847 static auto* cursors = new std::map<std::string, const wchar_t*>{
848 {"allScroll", IDC_SIZEALL},
849 {"basic", IDC_ARROW},
850 {"click", IDC_HAND},
851 {"forbidden", IDC_NO},
852 {"help", IDC_HELP},
853 {"move", IDC_SIZEALL},
854 {"none", nullptr},
855 {"noDrop", IDC_NO},
856 {"precise", IDC_CROSS},
857 {"progress", IDC_APPSTARTING},
858 {"text", IDC_IBEAM},
859 {"resizeColumn", IDC_SIZEWE},
860 {"resizeDown", IDC_SIZENS},
861 {"resizeDownLeft", IDC_SIZENESW},
862 {"resizeDownRight", IDC_SIZENWSE},
863 {"resizeLeft", IDC_SIZEWE},
864 {"resizeLeftRight", IDC_SIZEWE},
865 {"resizeRight", IDC_SIZEWE},
866 {"resizeRow", IDC_SIZENS},
867 {"resizeUp", IDC_SIZENS},
868 {"resizeUpDown", IDC_SIZENS},
869 {"resizeUpLeft", IDC_SIZENWSE},
870 {"resizeUpRight", IDC_SIZENESW},
871 {"resizeUpLeftDownRight", IDC_SIZENWSE},
872 {"resizeUpRightDownLeft", IDC_SIZENESW},
873 {"wait", IDC_WAIT},
874 };
875 const wchar_t* idc_name = IDC_ARROW;
876 auto it = cursors->find(cursor_name);
877 if (it != cursors->end()) {
878 idc_name = it->second;
879 }
880 return windows_proc_table_->LoadCursor(nullptr, idc_name);
881}
882
883FlutterWindowsView* FlutterWindowsEngine::GetViewFromTopLevelWindow(
884 HWND hwnd) const {
885 std::shared_lock read_lock(views_mutex_);
886 auto const iterator =
887 std::find_if(views_.begin(), views_.end(), [hwnd](auto const& pair) {
888 FlutterWindowsView* const view = pair.second;
889 return GetAncestor(view->GetWindowHandle(), GA_ROOT) == hwnd;
890 });
891 if (iterator != views_.end()) {
892 return iterator->second;
893 }
894 return nullptr;
895}
896
897void FlutterWindowsEngine::SendSystemLocales() {
898 std::vector<LanguageInfo> languages =
899 GetPreferredLanguageInfo(*windows_proc_table_);
900 std::vector<FlutterLocale> flutter_locales;
901 flutter_locales.reserve(languages.size());
902 for (const auto& info : languages) {
903 flutter_locales.push_back(CovertToFlutterLocale(info));
904 }
905 // Convert the locale list to the locale pointer list that must be provided.
906 std::vector<const FlutterLocale*> flutter_locale_list;
907 flutter_locale_list.reserve(flutter_locales.size());
908 std::transform(flutter_locales.begin(), flutter_locales.end(),
909 std::back_inserter(flutter_locale_list),
910 [](const auto& arg) -> const auto* { return &arg; });
911 embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
912 flutter_locale_list.size());
913}
914
915void FlutterWindowsEngine::InitializeKeyboard() {
916 auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
917 KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
918 KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
919 [](UINT virtual_key, bool extended) {
920 return MapVirtualKey(virtual_key,
921 extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
922 };
923 keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
924 internal_plugin_messenger, get_key_state, map_vk_to_scan));
925 text_input_plugin_ =
926 std::move(CreateTextInputPlugin(internal_plugin_messenger));
927}
928
929std::unique_ptr<KeyboardHandlerBase>
930FlutterWindowsEngine::CreateKeyboardKeyHandler(
931 BinaryMessenger* messenger,
934 auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
935 keyboard_key_handler->AddDelegate(
936 std::make_unique<KeyboardKeyEmbedderHandler>(
938 void* user_data) {
939 return SendKeyEvent(event, callback, user_data);
940 },
941 get_key_state, map_vk_to_scan));
942 keyboard_key_handler->AddDelegate(
943 std::make_unique<KeyboardKeyChannelHandler>(messenger));
944 keyboard_key_handler->InitKeyboardChannel();
945 return keyboard_key_handler;
946}
947
948std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
949 BinaryMessenger* messenger) {
950 return std::make_unique<TextInputPlugin>(messenger, this);
951}
952
953bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
954 return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
955 kSuccess);
956}
957
958bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
959 return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
960 kSuccess);
961}
962
963bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
964 int64_t texture_id) {
965 return (embedder_api_.MarkExternalTextureFrameAvailable(
966 engine_, texture_id) == kSuccess);
967}
968
969bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) const {
970 struct Captures {
972 };
973 auto captures = new Captures();
974 captures->callback = std::move(callback);
975 if (embedder_api_.PostRenderThreadTask(
976 engine_,
977 [](void* opaque) {
978 auto captures = reinterpret_cast<Captures*>(opaque);
979 captures->callback();
980 delete captures;
981 },
982 captures) == kSuccess) {
983 return true;
984 }
985 delete captures;
986 return false;
987}
988
989bool FlutterWindowsEngine::DispatchSemanticsAction(
991 uint64_t target,
993 fml::MallocMapping data) {
996 .view_id = view_id,
997 .node_id = target,
998 .action = action,
999 .data = data.GetMapping(),
1000 .data_length = data.GetSize(),
1001 };
1002 return (embedder_api_.SendSemanticsAction(engine_, &info));
1003}
1004
1005void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
1006 if (engine_ && semantics_enabled_ != enabled) {
1007 std::shared_lock read_lock(views_mutex_);
1008
1009 semantics_enabled_ = enabled;
1010 embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
1011 for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1012 iterator->second->UpdateSemanticsEnabled(enabled);
1013 }
1014 }
1015}
1016
1017void FlutterWindowsEngine::OnPreEngineRestart() {
1018 // Reset the keyboard's state on hot restart.
1019 InitializeKeyboard();
1020}
1021
1022std::string FlutterWindowsEngine::GetExecutableName() const {
1023 std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
1024 if (result.first) {
1025 const std::string& executable_path = result.second;
1026 size_t last_separator = executable_path.find_last_of("/\\");
1027 if (last_separator == std::string::npos ||
1028 last_separator == executable_path.size() - 1) {
1029 return executable_path;
1030 }
1031 return executable_path.substr(last_separator + 1);
1032 }
1033 return "Flutter";
1034}
1035
1036void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
1037 UpdateHighContrastMode();
1038}
1039
1040void FlutterWindowsEngine::UpdateHighContrastMode() {
1041 high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
1042
1043 SendAccessibilityFeatures();
1044 settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
1045}
1046
1047void FlutterWindowsEngine::SendAccessibilityFeatures() {
1048 int flags = 0;
1049
1050 if (high_contrast_enabled_) {
1051 flags |=
1053 }
1054
1055 embedder_api_.UpdateAccessibilityFeatures(
1056 engine_, static_cast<FlutterAccessibilityFeature>(flags));
1057}
1058
1059void FlutterWindowsEngine::RequestApplicationQuit(HWND hwnd,
1060 WPARAM wparam,
1061 LPARAM lparam,
1062 AppExitType exit_type) {
1063 platform_handler_->RequestAppExit(hwnd, wparam, lparam, exit_type, 0);
1064}
1065
1066void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
1067 std::optional<WPARAM> wparam,
1068 std::optional<LPARAM> lparam,
1069 UINT exit_code) {
1070 lifecycle_manager_->Quit(hwnd, wparam, lparam, exit_code);
1071}
1072
1073void FlutterWindowsEngine::OnDwmCompositionChanged() {
1074 if (display_manager_) {
1075 display_manager_->UpdateDisplays();
1076 }
1077
1078 std::shared_lock read_lock(views_mutex_);
1079 for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1080 iterator->second->OnDwmCompositionChanged();
1081 }
1082}
1083
1084void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
1085 WindowStateEvent event) {
1086 lifecycle_manager_->OnWindowStateEvent(hwnd, event);
1087}
1088
1089std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
1090 HWND hwnd,
1091 UINT message,
1092 WPARAM wparam,
1093 LPARAM lparam) {
1094 if (lifecycle_manager_) {
1095 return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam,
1096 lparam);
1097 }
1098 return std::nullopt;
1099}
1100
1101void FlutterWindowsEngine::UpdateFlutterCursor(
1102 const std::string& cursor_name) const {
1103 SetFlutterCursor(GetCursorByName(cursor_name));
1104}
1105
1106void FlutterWindowsEngine::SetFlutterCursor(HCURSOR cursor) const {
1107 windows_proc_table_->SetCursor(cursor);
1108}
1109
1110void FlutterWindowsEngine::OnChannelUpdate(std::string name, bool listening) {
1111 if (name == "flutter/platform" && listening) {
1112 lifecycle_manager_->BeginProcessingExit();
1113 } else if (name == "flutter/lifecycle" && listening) {
1114 lifecycle_manager_->BeginProcessingLifecycle();
1115 }
1116}
1117
1118void FlutterWindowsEngine::OnViewFocusChangeRequest(
1119 const FlutterViewFocusChangeRequest* request) {
1120 std::shared_lock read_lock(views_mutex_);
1121
1122 auto iterator = views_.find(request->view_id);
1123 if (iterator == views_.end()) {
1124 return;
1125 }
1126
1127 FlutterWindowsView* view = iterator->second;
1128 view->Focus();
1129}
1130
1131bool FlutterWindowsEngine::Present(const FlutterPresentViewInfo* info) {
1132 // This runs on the raster thread. Lock the views map for the entirety of the
1133 // present operation to block the platform thread from destroying the
1134 // view during the present.
1135 std::shared_lock read_lock(views_mutex_);
1136
1137 auto iterator = views_.find(info->view_id);
1138 if (iterator == views_.end()) {
1139 return false;
1140 }
1141
1142 FlutterWindowsView* view = iterator->second;
1143
1144 return compositor_->Present(view, info->layers, info->layers_count);
1145}
1146
1147bool FlutterWindowsEngine::HandleDisplayMonitorMessage(HWND hwnd,
1148 UINT message,
1149 WPARAM wparam,
1150 LPARAM lparam,
1151 LRESULT* result) {
1152 if (!display_manager_) {
1153 return false;
1154 }
1155
1156 return display_manager_->HandleWindowMessage(hwnd, message, wparam, lparam,
1157 result);
1158}
1159
1160} // namespace flutter
static void SetUp(BinaryMessenger *binary_messenger, AccessibilityPlugin *plugin)
FlutterWindowsEngine(const FlutterProjectBundle &project, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
void SetSwitches(const std::vector< std::string > &switches)
std::function< SHORT(UINT, bool)> MapVirtualKeyToScanCode
static std::unique_ptr< Manager > Create(GpuPreference gpu_preference)
Definition manager.cc:17
static std::shared_ptr< ProcTable > Create()
Definition proc_table.cc:12
A Mapping like NonOwnedMapping, but uses Free as its release proc.
Definition mapping.h:144
int32_t value
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
Definition embedder.cc:3741
@ kOpenGL
Definition embedder.h:80
FlutterAccessibilityFeature
Definition embedder.h:91
@ kFlutterAccessibilityFeatureHighContrast
Request that UI be rendered with darker colors.
Definition embedder.h:105
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2379
FlutterSemanticsAction
Definition embedder.h:122
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1482
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
#define SAFE_ACCESS(pointer, member, default_value)
GLFWwindow * window
Definition main.cc:60
FlView * view
const char * message
if(engine==nullptr)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
const gchar * channel
uint32_t * target
G_BEGIN_DECLS FlutterViewId view_id
static FlutterDesktopMessage ConvertToDesktopMessage(const FlutterPlatformMessage &engine_message)
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
void(* FlutterDesktopOnPluginRegistrarDestroyed)(FlutterDesktopPluginRegistrarRef)
static constexpr char kAccessibilityChannelName[]
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
#define FML_UNREACHABLE()
Definition logging.h:128
#define FML_DCHECK(condition)
Definition logging.h:122
const char * name
Definition fuchsia.cc:50
static constexpr FlutterViewId kImplicitViewId
char ** argv
Definition library.h:9
FlTexture * texture
WindowStateEvent
An event representing a change in window state that may update the.
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service host
Definition switch_defs.h:69
std::vector< LanguageInfo > GetPreferredLanguageInfo()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
int64_t FlutterViewId
std::pair< bool, std::string > GetExecutablePath()
std::string PathToUtf8(const std::filesystem::path &path)
std::function< void()> closure
Definition closure.h:14
Definition ref_ptr.h:261
std::vector< FlutterEngineDisplay > * displays
int32_t height
int32_t width
FlutterAddViewCallback add_view_callback
Definition embedder.h:1147
FlutterViewId view_id
The identifier for the view to add. This must be unique.
Definition embedder.h:1127
const FlutterWindowMetricsEvent * view_metrics
Definition embedder.h:1132
An update to whether a message channel has a listener set or not.
Definition embedder.h:1884
void(* thread_priority_setter)(FlutterThreadPriority)
Definition embedder.h:1959
const FlutterTaskRunnerDescription * ui_task_runner
Definition embedder.h:1963
const FlutterTaskRunnerDescription * platform_task_runner
Definition embedder.h:1951
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
Definition embedder.h:1946
Function-pointer-based versions of the APIs above.
Definition embedder.h:3763
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineProcs).
Definition embedder.h:3765
FlutterEngineGetCurrentTimeFnPtr GetCurrentTime
Definition embedder.h:3796
FlutterEngineRunTaskFnPtr RunTask
Definition embedder.h:3797
const char * language_code
Definition embedder.h:2314
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:2310
const char * script_code
Definition embedder.h:2324
const char * country_code
Definition embedder.h:2319
ProcResolver gl_proc_resolver
Definition embedder.h:765
size_t struct_size
The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig).
Definition embedder.h:726
TextureFrameCallback gl_external_texture_frame_callback
Definition embedder.h:770
BoolCallback make_resource_current
Definition embedder.h:748
UIntFrameInfoCallback fbo_with_frame_info_callback
Definition embedder.h:778
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1491
const FlutterPlatformMessageResponseHandle * response_handle
Definition embedder.h:1501
const char * channel
Definition embedder.h:1492
const uint8_t * message
Definition embedder.h:1493
size_t layers_count
The count of layers.
Definition embedder.h:2225
FlutterViewId view_id
The identifier of the target view.
Definition embedder.h:2219
const FlutterLayer ** layers
The layers that should be composited onto the view.
Definition embedder.h:2222
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2513
FlutterRemoveViewCallback remove_view_callback
Definition embedder.h:1195
FlutterViewId view_id
Definition embedder.h:1178
FlutterSoftwareRendererConfig software
Definition embedder.h:1041
FlutterOpenGLRendererConfig open_gl
Definition embedder.h:1040
FlutterRendererType type
Definition embedder.h:1038
A batch of updates to semantics nodes and custom actions.
Definition embedder.h:1851
size_t node_count
The number of semantics node updates.
Definition embedder.h:1855
size_t custom_action_count
The number of semantics custom action updates.
Definition embedder.h:1859
FlutterSemanticsNode2 ** nodes
Definition embedder.h:1857
FlutterSemanticsCustomAction2 ** custom_actions
Definition embedder.h:1862
FlutterViewId view_id
Definition embedder.h:1864
size_t struct_size
The size of this struct. Must be sizeof(FlutterSoftwareRendererConfig).
Definition embedder.h:1029
SoftwareSurfacePresentCallback surface_present_callback
Definition embedder.h:1034
size_t struct_size
The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
Definition embedder.h:1919
BoolCallback runs_task_on_current_thread_callback
Definition embedder.h:1925
FlutterTaskRunnerPostTaskCallback post_task_callback
Definition embedder.h:1936
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1257
int64_t texture_id
#define BASE_DCHECK(condition)
Definition logging.h:63
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
UINT_PTR WPARAM