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 std::transform(
294 switches.begin(), switches.end(), std::back_inserter(argv),
295 [](const std::string& arg) -> const char* { return arg.c_str(); });
296
297 const std::vector<std::string>& entrypoint_args =
298 project_->dart_entrypoint_arguments();
299 std::vector<const char*> entrypoint_argv;
300 std::transform(
301 entrypoint_args.begin(), entrypoint_args.end(),
302 std::back_inserter(entrypoint_argv),
303 [](const std::string& arg) -> const char* { return arg.c_str(); });
304
305 // Configure task runners.
306 FlutterTaskRunnerDescription platform_task_runner = {};
307 platform_task_runner.struct_size = sizeof(FlutterTaskRunnerDescription);
308 platform_task_runner.user_data = task_runner_.get();
309 platform_task_runner.runs_task_on_current_thread_callback =
310 [](void* user_data) -> bool {
311 return static_cast<TaskRunner*>(user_data)->RunsTasksOnCurrentThread();
312 };
313 platform_task_runner.post_task_callback = [](FlutterTask task,
314 uint64_t target_time_nanos,
315 void* user_data) -> void {
316 static_cast<TaskRunner*>(user_data)->PostFlutterTask(task,
317 target_time_nanos);
318 };
319 FlutterCustomTaskRunners custom_task_runners = {};
320 custom_task_runners.struct_size = sizeof(FlutterCustomTaskRunners);
321 custom_task_runners.platform_task_runner = &platform_task_runner;
322 custom_task_runners.thread_priority_setter =
323 &WindowsPlatformThreadPrioritySetter;
324
325 if (project_->ui_thread_policy() !=
326 FlutterUIThreadPolicy::RunOnSeparateThread) {
327 custom_task_runners.ui_task_runner = &platform_task_runner;
328 } else {
329 FML_LOG(WARNING) << "Running with unmerged platform and UI threads. This "
330 "will be removed in future.";
331 }
332
335 args.shutdown_dart_vm_when_done = true;
336 args.assets_path = assets_path_string.c_str();
337 args.icu_data_path = icu_path_string.c_str();
338 args.command_line_argc = static_cast<int>(argv.size());
339 args.command_line_argv = argv.empty() ? nullptr : argv.data();
340 args.engine_id = reinterpret_cast<int64_t>(this);
341
342 // Fail if conflicting non-default entrypoints are specified in the method
343 // argument and the project.
344 //
345 // TODO(cbracken): https://github.com/flutter/flutter/issues/109285
346 // The entrypoint method parameter should eventually be removed from this
347 // method and only the entrypoint specified in project_ should be used.
348 if (!project_->dart_entrypoint().empty() && !entrypoint.empty() &&
349 project_->dart_entrypoint() != entrypoint) {
350 FML_LOG(ERROR) << "Conflicting entrypoints were specified in "
351 "FlutterDesktopEngineProperties.dart_entrypoint and "
352 "FlutterDesktopEngineRun(engine, entry_point). ";
353 return false;
354 }
355 if (!entrypoint.empty()) {
356 args.custom_dart_entrypoint = entrypoint.data();
357 } else if (!project_->dart_entrypoint().empty()) {
358 args.custom_dart_entrypoint = project_->dart_entrypoint().c_str();
359 }
360 args.dart_entrypoint_argc = static_cast<int>(entrypoint_argv.size());
361 args.dart_entrypoint_argv =
362 entrypoint_argv.empty() ? nullptr : entrypoint_argv.data();
363 args.platform_message_callback =
364 [](const FlutterPlatformMessage* engine_message,
365 void* user_data) -> void {
366 auto host = static_cast<FlutterWindowsEngine*>(user_data);
367 return host->HandlePlatformMessage(engine_message);
368 };
369 args.vsync_callback = [](void* user_data, intptr_t baton) -> void {
370 auto host = static_cast<FlutterWindowsEngine*>(user_data);
371 host->OnVsync(baton);
372 };
373 args.on_pre_engine_restart_callback = [](void* user_data) {
374 auto host = static_cast<FlutterWindowsEngine*>(user_data);
375 host->OnPreEngineRestart();
376 };
377 args.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
378 void* user_data) {
379 auto host = static_cast<FlutterWindowsEngine*>(user_data);
380
381 auto view = host->view(update->view_id);
382 if (!view) {
383 return;
384 }
385
386 auto accessibility_bridge = view->accessibility_bridge().lock();
387 if (!accessibility_bridge) {
388 return;
389 }
390
391 for (size_t i = 0; i < update->node_count; i++) {
392 const FlutterSemanticsNode2* node = update->nodes[i];
393 accessibility_bridge->AddFlutterSemanticsNodeUpdate(*node);
394 }
395
396 for (size_t i = 0; i < update->custom_action_count; i++) {
398 accessibility_bridge->AddFlutterSemanticsCustomActionUpdate(*action);
399 }
400
401 accessibility_bridge->CommitUpdates();
402 };
403 args.root_isolate_create_callback = [](void* user_data) {
404 auto host = static_cast<FlutterWindowsEngine*>(user_data);
405 if (host->root_isolate_create_callback_) {
406 host->root_isolate_create_callback_();
407 }
408 };
409 args.channel_update_callback = [](const FlutterChannelUpdate* update,
410 void* user_data) {
411 auto host = static_cast<FlutterWindowsEngine*>(user_data);
412 if (SAFE_ACCESS(update, channel, nullptr) != nullptr) {
413 std::string channel_name(update->channel);
414 host->OnChannelUpdate(std::move(channel_name),
415 SAFE_ACCESS(update, listening, false));
416 }
417 };
418 args.view_focus_change_request_callback =
419 [](const FlutterViewFocusChangeRequest* request, void* user_data) {
420 auto host = static_cast<FlutterWindowsEngine*>(user_data);
421 host->OnViewFocusChangeRequest(request);
422 };
423
424 args.custom_task_runners = &custom_task_runners;
425
426 if (!platform_view_plugin_) {
427 platform_view_plugin_ = std::make_unique<PlatformViewPlugin>(
428 messenger_wrapper_.get(), task_runner_.get());
429 }
430 if (egl_manager_) {
431 auto resolver = [](const char* name) -> void* {
432 return reinterpret_cast<void*>(::eglGetProcAddress(name));
433 };
434
435 // TODO(schectman) Pass the platform view manager to the compositor
436 // constructors: https://github.com/flutter/flutter/issues/143375
437 compositor_ =
438 std::make_unique<CompositorOpenGL>(this, resolver, enable_impeller_);
439 } else {
440 compositor_ = std::make_unique<CompositorSoftware>();
441 }
442
443 FlutterCompositor compositor = {};
444 compositor.struct_size = sizeof(FlutterCompositor);
445 compositor.user_data = this;
446 compositor.create_backing_store_callback =
447 [](const FlutterBackingStoreConfig* config,
448 FlutterBackingStore* backing_store_out, void* user_data) -> bool {
449 auto host = static_cast<FlutterWindowsEngine*>(user_data);
450
451 return host->compositor_->CreateBackingStore(*config, backing_store_out);
452 };
453
454 compositor.collect_backing_store_callback =
455 [](const FlutterBackingStore* backing_store, void* user_data) -> bool {
456 auto host = static_cast<FlutterWindowsEngine*>(user_data);
457
458 return host->compositor_->CollectBackingStore(backing_store);
459 };
460
461 compositor.present_view_callback =
462 [](const FlutterPresentViewInfo* info) -> bool {
463 auto host = static_cast<FlutterWindowsEngine*>(info->user_data);
464
465 return host->Present(info);
466 };
467 args.compositor = &compositor;
468
469 if (aot_data_) {
470 args.aot_data = aot_data_.get();
471 }
472
473 // The platform thread creates OpenGL contexts. These
474 // must be released to be used by the engine's threads.
475 FML_DCHECK(!egl_manager_ || !egl_manager_->HasContextCurrent());
476
477 FlutterRendererConfig renderer_config;
478
479 if (enable_impeller_) {
480 // Impeller does not support a Software backend. Avoid falling back and
481 // confusing the engine on which renderer is selected.
482 if (!egl_manager_) {
483 FML_LOG(ERROR) << "Could not create surface manager. Impeller backend "
484 "does not support software rendering.";
485 return false;
486 }
487 renderer_config = GetOpenGLRendererConfig();
488 } else {
489 renderer_config =
490 egl_manager_ ? GetOpenGLRendererConfig() : GetSoftwareRendererConfig();
491 }
492
493 auto result = embedder_api_.Run(FLUTTER_ENGINE_VERSION, &renderer_config,
494 &args, this, &engine_);
495 if (result != kSuccess || engine_ == nullptr) {
496 FML_LOG(ERROR) << "Failed to start Flutter engine: error " << result;
497 return false;
498 }
499
500 display_manager_->UpdateDisplays();
501
502 SendSystemLocales();
503
504 settings_plugin_->StartWatching();
505 settings_plugin_->SendSettings();
506
507 InitializeKeyboard();
508
509 return true;
510}
511
512bool FlutterWindowsEngine::Stop() {
513 if (engine_) {
514 window_manager_->OnEngineShutdown();
515 for (const auto& [callback, registrar] :
516 plugin_registrar_destruction_callbacks_) {
517 callback(registrar);
518 }
519 FlutterEngineResult result = embedder_api_.Shutdown(engine_);
520 engine_ = nullptr;
521 return (result == kSuccess);
522 }
523 return false;
524}
525
526std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView(
527 std::unique_ptr<WindowBindingHandler> window,
528 bool is_sized_to_content,
529 const BoxConstraints& box_constraints,
530 FlutterWindowsViewSizingDelegate* sizing_delegate) {
531 auto view_id = next_view_id_;
532 auto view = std::make_unique<FlutterWindowsView>(
533 view_id, this, std::move(window), is_sized_to_content, box_constraints,
534 sizing_delegate, windows_proc_table_);
535
536 view->CreateRenderSurface();
537 view->UpdateSemanticsEnabled(semantics_enabled_);
538
539 next_view_id_++;
540
541 {
542 // Add the view to the embedder. This must happen before the engine
543 // is notified the view exists and starts presenting to it.
544 std::unique_lock write_lock(views_mutex_);
545 FML_DCHECK(views_.find(view_id) == views_.end());
546 views_[view_id] = view.get();
547 }
548
549 if (!view->IsImplicitView()) {
550 FML_DCHECK(running());
551
552 struct Captures {
554 bool added;
555 };
556 Captures captures = {};
557
558 FlutterWindowMetricsEvent metrics = view->CreateWindowMetricsEvent();
559
560 FlutterAddViewInfo info = {};
561 info.struct_size = sizeof(FlutterAddViewInfo);
562 info.view_id = view_id;
563 info.view_metrics = &metrics;
564 info.user_data = &captures;
565 info.add_view_callback = [](const FlutterAddViewResult* result) {
566 Captures* captures = reinterpret_cast<Captures*>(result->user_data);
567 captures->added = result->added;
568 captures->latch.Signal();
569 };
570
571 FlutterEngineResult result = embedder_api_.AddView(engine_, &info);
572 if (result != kSuccess) {
573 FML_LOG(ERROR)
574 << "Starting the add view operation failed. FlutterEngineAddView "
575 "returned an unexpected result: "
576 << result << ". This indicates a bug in the Windows embedder.";
577 FML_DCHECK(false);
578 return nullptr;
579 }
580
581 // Block the platform thread until the engine has added the view.
582 // TODO(loicsharma): This blocks the platform thread eagerly and can
583 // cause unnecessary delay in input processing. Instead, this should block
584 // lazily only when the app does an operation which needs the view.
585 // https://github.com/flutter/flutter/issues/146248
586 captures.latch.Wait();
587
588 if (!captures.added) {
589 // Adding the view failed. Update the embedder's state to match the
590 // engine's state. This is unexpected and indicates a bug in the Windows
591 // embedder.
592 FML_LOG(ERROR) << "FlutterEngineAddView failed to add view";
593 std::unique_lock write_lock(views_mutex_);
594 views_.erase(view_id);
595 return nullptr;
596 }
597 }
598
599 return std::move(view);
600}
601
602void FlutterWindowsEngine::RemoveView(FlutterViewId view_id) {
603 FML_DCHECK(running());
604
605 // Notify the engine to stop rendering to the view if it isn't the implicit
606 // view. The engine and framework assume the implicit view always exists and
607 // can continue presenting.
608 if (view_id != kImplicitViewId) {
609 struct Captures {
611 bool removed;
612 };
613 Captures captures = {};
614
615 FlutterRemoveViewInfo info = {};
616 info.struct_size = sizeof(FlutterRemoveViewInfo);
617 info.view_id = view_id;
618 info.user_data = &captures;
619 info.remove_view_callback = [](const FlutterRemoveViewResult* result) {
620 // This is invoked on an engine thread. If
621 // |FlutterRemoveViewResult.removed| is `true`, the engine guarantees the
622 // view won't be presented.
623 Captures* captures = reinterpret_cast<Captures*>(result->user_data);
624 captures->removed = result->removed;
625 captures->latch.Signal();
626 };
627
628 FlutterEngineResult result = embedder_api_.RemoveView(engine_, &info);
629 if (result != kSuccess) {
630 FML_LOG(ERROR) << "Starting the remove view operation failed. "
631 "FlutterEngineRemoveView "
632 "returned an unexpected result: "
633 << result
634 << ". This indicates a bug in the Windows embedder.";
635 FML_DCHECK(false);
636 return;
637 }
638
639 // Block the platform thread until the engine has removed the view.
640 // TODO(loicsharma): This blocks the platform thread eagerly and can
641 // cause unnecessary delay in input processing. Instead, this should block
642 // lazily only when an operation needs the view.
643 // https://github.com/flutter/flutter/issues/146248
644 captures.latch.Wait();
645
646 if (!captures.removed) {
647 // Removing the view failed. This is unexpected and indicates a bug in the
648 // Windows embedder.
649 FML_LOG(ERROR) << "FlutterEngineRemoveView failed to remove view";
650 return;
651 }
652 }
653
654 {
655 // The engine no longer presents to the view. Remove the view from the
656 // embedder.
657 std::unique_lock write_lock(views_mutex_);
658
659 FML_DCHECK(views_.find(view_id) != views_.end());
660 views_.erase(view_id);
661 }
662}
663
664void FlutterWindowsEngine::OnVsync(intptr_t baton) {
665 std::chrono::nanoseconds current_time =
666 std::chrono::nanoseconds(embedder_api_.GetCurrentTime());
667 std::chrono::nanoseconds frame_interval = FrameInterval();
668 auto next = SnapToNextTick(current_time, start_time_, frame_interval);
669 embedder_api_.OnVsync(engine_, baton, next.count(),
670 (next + frame_interval).count());
671}
672
673std::chrono::nanoseconds FlutterWindowsEngine::FrameInterval() {
674 if (frame_interval_override_.has_value()) {
675 return frame_interval_override_.value();
676 }
677 uint64_t interval = 16600000;
678
679 DWM_TIMING_INFO timing_info = {};
680 timing_info.cbSize = sizeof(timing_info);
681 HRESULT result = DwmGetCompositionTimingInfo(NULL, &timing_info);
682 if (result == S_OK && timing_info.rateRefresh.uiDenominator > 0 &&
683 timing_info.rateRefresh.uiNumerator > 0) {
684 interval = static_cast<double>(timing_info.rateRefresh.uiDenominator *
685 1000000000.0) /
686 static_cast<double>(timing_info.rateRefresh.uiNumerator);
687 }
688
689 return std::chrono::nanoseconds(interval);
690}
691
692FlutterWindowsView* FlutterWindowsEngine::view(FlutterViewId view_id) const {
693 std::shared_lock read_lock(views_mutex_);
694
695 auto iterator = views_.find(view_id);
696 if (iterator == views_.end()) {
697 return nullptr;
698 }
699
700 return iterator->second;
701}
702
703// Returns the currently configured Plugin Registrar.
704FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
705 return plugin_registrar_.get();
706}
707
708void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
711 plugin_registrar_destruction_callbacks_[callback] = registrar;
712}
713
714void FlutterWindowsEngine::UpdateDisplay(
715 const std::vector<FlutterEngineDisplay>& displays) {
716 if (engine_) {
717 embedder_api_.NotifyDisplayUpdate(engine_,
719 displays.data(), displays.size());
720 }
721}
722
723void FlutterWindowsEngine::SendWindowMetricsEvent(
724 const FlutterWindowMetricsEvent& event) {
725 if (engine_) {
726 embedder_api_.SendWindowMetricsEvent(engine_, &event);
727 }
728}
729
730void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
731 if (engine_) {
732 embedder_api_.SendPointerEvent(engine_, &event, 1);
733 }
734}
735
736void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
738 void* user_data) {
739 if (engine_) {
740 embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
741 }
742}
743
744void FlutterWindowsEngine::SendViewFocusEvent(
745 const FlutterViewFocusEvent& event) {
746 if (engine_) {
747 embedder_api_.SendViewFocusEvent(engine_, &event);
748 }
749}
750
751bool FlutterWindowsEngine::SendPlatformMessage(
752 const char* channel,
753 const uint8_t* message,
754 const size_t message_size,
755 const FlutterDesktopBinaryReply reply,
756 void* user_data) {
757 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
758 if (reply != nullptr && user_data != nullptr) {
759 FlutterEngineResult result =
760 embedder_api_.PlatformMessageCreateResponseHandle(
761 engine_, reply, user_data, &response_handle);
762 if (result != kSuccess) {
763 FML_LOG(ERROR) << "Failed to create response handle";
764 return false;
765 }
766 }
767
768 FlutterPlatformMessage platform_message = {
770 channel,
771 message,
772 message_size,
773 response_handle,
774 };
775
776 FlutterEngineResult message_result =
777 embedder_api_.SendPlatformMessage(engine_, &platform_message);
778 if (response_handle != nullptr) {
779 embedder_api_.PlatformMessageReleaseResponseHandle(engine_,
780 response_handle);
781 }
782 return message_result == kSuccess;
783}
784
785void FlutterWindowsEngine::SendPlatformMessageResponse(
787 const uint8_t* data,
788 size_t data_length) {
789 embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
790}
791
792void FlutterWindowsEngine::HandlePlatformMessage(
793 const FlutterPlatformMessage* engine_message) {
794 if (engine_message->struct_size != sizeof(FlutterPlatformMessage)) {
795 FML_LOG(ERROR) << "Invalid message size received. Expected: "
796 << sizeof(FlutterPlatformMessage) << " but received "
797 << engine_message->struct_size;
798 return;
799 }
800
801 auto message = ConvertToDesktopMessage(*engine_message);
802
803 message_dispatcher_->HandleMessage(message, [this] {}, [this] {});
804}
805
806void FlutterWindowsEngine::ReloadSystemFonts() {
807 embedder_api_.ReloadSystemFonts(engine_);
808}
809
810void FlutterWindowsEngine::ScheduleFrame() {
811 embedder_api_.ScheduleFrame(engine_);
812}
813
814void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) {
815 next_frame_callback_ = std::move(callback);
816
817 embedder_api_.SetNextFrameCallback(
818 engine_,
819 [](void* user_data) {
820 // Embedder callback runs on raster thread. Switch back to platform
821 // thread.
823 static_cast<FlutterWindowsEngine*>(user_data);
824
825 self->task_runner_->PostTask(std::move(self->next_frame_callback_));
826 },
827 this);
828}
829
830HCURSOR FlutterWindowsEngine::GetCursorByName(
831 const std::string& cursor_name) const {
832 static auto* cursors = new std::map<std::string, const wchar_t*>{
833 {"allScroll", IDC_SIZEALL},
834 {"basic", IDC_ARROW},
835 {"click", IDC_HAND},
836 {"forbidden", IDC_NO},
837 {"help", IDC_HELP},
838 {"move", IDC_SIZEALL},
839 {"none", nullptr},
840 {"noDrop", IDC_NO},
841 {"precise", IDC_CROSS},
842 {"progress", IDC_APPSTARTING},
843 {"text", IDC_IBEAM},
844 {"resizeColumn", IDC_SIZEWE},
845 {"resizeDown", IDC_SIZENS},
846 {"resizeDownLeft", IDC_SIZENESW},
847 {"resizeDownRight", IDC_SIZENWSE},
848 {"resizeLeft", IDC_SIZEWE},
849 {"resizeLeftRight", IDC_SIZEWE},
850 {"resizeRight", IDC_SIZEWE},
851 {"resizeRow", IDC_SIZENS},
852 {"resizeUp", IDC_SIZENS},
853 {"resizeUpDown", IDC_SIZENS},
854 {"resizeUpLeft", IDC_SIZENWSE},
855 {"resizeUpRight", IDC_SIZENESW},
856 {"resizeUpLeftDownRight", IDC_SIZENWSE},
857 {"resizeUpRightDownLeft", IDC_SIZENESW},
858 {"wait", IDC_WAIT},
859 };
860 const wchar_t* idc_name = IDC_ARROW;
861 auto it = cursors->find(cursor_name);
862 if (it != cursors->end()) {
863 idc_name = it->second;
864 }
865 return windows_proc_table_->LoadCursor(nullptr, idc_name);
866}
867
868FlutterWindowsView* FlutterWindowsEngine::GetViewFromTopLevelWindow(
869 HWND hwnd) const {
870 std::shared_lock read_lock(views_mutex_);
871 auto const iterator =
872 std::find_if(views_.begin(), views_.end(), [hwnd](auto const& pair) {
873 FlutterWindowsView* const view = pair.second;
874 return GetAncestor(view->GetWindowHandle(), GA_ROOT) == hwnd;
875 });
876 if (iterator != views_.end()) {
877 return iterator->second;
878 }
879 return nullptr;
880}
881
882void FlutterWindowsEngine::SendSystemLocales() {
883 std::vector<LanguageInfo> languages =
884 GetPreferredLanguageInfo(*windows_proc_table_);
885 std::vector<FlutterLocale> flutter_locales;
886 flutter_locales.reserve(languages.size());
887 for (const auto& info : languages) {
888 flutter_locales.push_back(CovertToFlutterLocale(info));
889 }
890 // Convert the locale list to the locale pointer list that must be provided.
891 std::vector<const FlutterLocale*> flutter_locale_list;
892 flutter_locale_list.reserve(flutter_locales.size());
893 std::transform(flutter_locales.begin(), flutter_locales.end(),
894 std::back_inserter(flutter_locale_list),
895 [](const auto& arg) -> const auto* { return &arg; });
896 embedder_api_.UpdateLocales(engine_, flutter_locale_list.data(),
897 flutter_locale_list.size());
898}
899
900void FlutterWindowsEngine::InitializeKeyboard() {
901 auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
902 KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState;
903 KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan =
904 [](UINT virtual_key, bool extended) {
905 return MapVirtualKey(virtual_key,
906 extended ? MAPVK_VK_TO_VSC_EX : MAPVK_VK_TO_VSC);
907 };
908 keyboard_key_handler_ = std::move(CreateKeyboardKeyHandler(
909 internal_plugin_messenger, get_key_state, map_vk_to_scan));
910 text_input_plugin_ =
911 std::move(CreateTextInputPlugin(internal_plugin_messenger));
912}
913
914std::unique_ptr<KeyboardHandlerBase>
915FlutterWindowsEngine::CreateKeyboardKeyHandler(
916 BinaryMessenger* messenger,
919 auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
920 keyboard_key_handler->AddDelegate(
921 std::make_unique<KeyboardKeyEmbedderHandler>(
923 void* user_data) {
924 return SendKeyEvent(event, callback, user_data);
925 },
926 get_key_state, map_vk_to_scan));
927 keyboard_key_handler->AddDelegate(
928 std::make_unique<KeyboardKeyChannelHandler>(messenger));
929 keyboard_key_handler->InitKeyboardChannel();
930 return keyboard_key_handler;
931}
932
933std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
934 BinaryMessenger* messenger) {
935 return std::make_unique<TextInputPlugin>(messenger, this);
936}
937
938bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
939 return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
940 kSuccess);
941}
942
943bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
944 return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
945 kSuccess);
946}
947
948bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
949 int64_t texture_id) {
950 return (embedder_api_.MarkExternalTextureFrameAvailable(
951 engine_, texture_id) == kSuccess);
952}
953
954bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) const {
955 struct Captures {
957 };
958 auto captures = new Captures();
959 captures->callback = std::move(callback);
960 if (embedder_api_.PostRenderThreadTask(
961 engine_,
962 [](void* opaque) {
963 auto captures = reinterpret_cast<Captures*>(opaque);
964 captures->callback();
965 delete captures;
966 },
967 captures) == kSuccess) {
968 return true;
969 }
970 delete captures;
971 return false;
972}
973
974bool FlutterWindowsEngine::DispatchSemanticsAction(
976 uint64_t target,
981 .view_id = view_id,
982 .node_id = target,
983 .action = action,
984 .data = data.GetMapping(),
985 .data_length = data.GetSize(),
986 };
987 return (embedder_api_.SendSemanticsAction(engine_, &info));
988}
989
990void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) {
991 if (engine_ && semantics_enabled_ != enabled) {
992 std::shared_lock read_lock(views_mutex_);
993
994 semantics_enabled_ = enabled;
995 embedder_api_.UpdateSemanticsEnabled(engine_, enabled);
996 for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
997 iterator->second->UpdateSemanticsEnabled(enabled);
998 }
999 }
1000}
1001
1002void FlutterWindowsEngine::OnPreEngineRestart() {
1003 // Reset the keyboard's state on hot restart.
1004 InitializeKeyboard();
1005}
1006
1007std::string FlutterWindowsEngine::GetExecutableName() const {
1008 std::pair<bool, std::string> result = fml::paths::GetExecutablePath();
1009 if (result.first) {
1010 const std::string& executable_path = result.second;
1011 size_t last_separator = executable_path.find_last_of("/\\");
1012 if (last_separator == std::string::npos ||
1013 last_separator == executable_path.size() - 1) {
1014 return executable_path;
1015 }
1016 return executable_path.substr(last_separator + 1);
1017 }
1018 return "Flutter";
1019}
1020
1021void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
1022 UpdateHighContrastMode();
1023}
1024
1025void FlutterWindowsEngine::UpdateHighContrastMode() {
1026 high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
1027
1028 SendAccessibilityFeatures();
1029 settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
1030}
1031
1032void FlutterWindowsEngine::SendAccessibilityFeatures() {
1033 int flags = 0;
1034
1035 if (high_contrast_enabled_) {
1036 flags |=
1038 }
1039
1040 embedder_api_.UpdateAccessibilityFeatures(
1041 engine_, static_cast<FlutterAccessibilityFeature>(flags));
1042}
1043
1044void FlutterWindowsEngine::RequestApplicationQuit(HWND hwnd,
1045 WPARAM wparam,
1046 LPARAM lparam,
1047 AppExitType exit_type) {
1048 platform_handler_->RequestAppExit(hwnd, wparam, lparam, exit_type, 0);
1049}
1050
1051void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
1052 std::optional<WPARAM> wparam,
1053 std::optional<LPARAM> lparam,
1054 UINT exit_code) {
1055 lifecycle_manager_->Quit(hwnd, wparam, lparam, exit_code);
1056}
1057
1058void FlutterWindowsEngine::OnDwmCompositionChanged() {
1059 if (display_manager_) {
1060 display_manager_->UpdateDisplays();
1061 }
1062
1063 std::shared_lock read_lock(views_mutex_);
1064 for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
1065 iterator->second->OnDwmCompositionChanged();
1066 }
1067}
1068
1069void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
1070 WindowStateEvent event) {
1071 lifecycle_manager_->OnWindowStateEvent(hwnd, event);
1072}
1073
1074std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
1075 HWND hwnd,
1076 UINT message,
1077 WPARAM wparam,
1078 LPARAM lparam) {
1079 if (lifecycle_manager_) {
1080 return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam,
1081 lparam);
1082 }
1083 return std::nullopt;
1084}
1085
1086void FlutterWindowsEngine::UpdateFlutterCursor(
1087 const std::string& cursor_name) const {
1088 SetFlutterCursor(GetCursorByName(cursor_name));
1089}
1090
1091void FlutterWindowsEngine::SetFlutterCursor(HCURSOR cursor) const {
1092 windows_proc_table_->SetCursor(cursor);
1093}
1094
1095void FlutterWindowsEngine::OnChannelUpdate(std::string name, bool listening) {
1096 if (name == "flutter/platform" && listening) {
1097 lifecycle_manager_->BeginProcessingExit();
1098 } else if (name == "flutter/lifecycle" && listening) {
1099 lifecycle_manager_->BeginProcessingLifecycle();
1100 }
1101}
1102
1103void FlutterWindowsEngine::OnViewFocusChangeRequest(
1104 const FlutterViewFocusChangeRequest* request) {
1105 std::shared_lock read_lock(views_mutex_);
1106
1107 auto iterator = views_.find(request->view_id);
1108 if (iterator == views_.end()) {
1109 return;
1110 }
1111
1112 FlutterWindowsView* view = iterator->second;
1113 view->Focus();
1114}
1115
1116bool FlutterWindowsEngine::Present(const FlutterPresentViewInfo* info) {
1117 // This runs on the raster thread. Lock the views map for the entirety of the
1118 // present operation to block the platform thread from destroying the
1119 // view during the present.
1120 std::shared_lock read_lock(views_mutex_);
1121
1122 auto iterator = views_.find(info->view_id);
1123 if (iterator == views_.end()) {
1124 return false;
1125 }
1126
1127 FlutterWindowsView* view = iterator->second;
1128
1129 return compositor_->Present(view, info->layers, info->layers_count);
1130}
1131
1132bool FlutterWindowsEngine::HandleDisplayMonitorMessage(HWND hwnd,
1133 UINT message,
1134 WPARAM wparam,
1135 LPARAM lparam,
1136 LRESULT* result) {
1137 if (!display_manager_) {
1138 return false;
1139 }
1140
1141 return display_manager_->HandleWindowMessage(hwnd, message, wparam, lparam,
1142 result);
1143}
1144
1145} // 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:3716
@ 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:2354
FlutterSemanticsAction
Definition embedder.h:122
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1457
#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:49
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()
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:1859
void(* thread_priority_setter)(FlutterThreadPriority)
Definition embedder.h:1934
const FlutterTaskRunnerDescription * ui_task_runner
Definition embedder.h:1938
const FlutterTaskRunnerDescription * platform_task_runner
Definition embedder.h:1926
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
Definition embedder.h:1921
Function-pointer-based versions of the APIs above.
Definition embedder.h:3734
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineProcs).
Definition embedder.h:3736
FlutterEngineGetCurrentTimeFnPtr GetCurrentTime
Definition embedder.h:3767
FlutterEngineRunTaskFnPtr RunTask
Definition embedder.h:3768
const char * language_code
Definition embedder.h:2289
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:2285
const char * script_code
Definition embedder.h:2299
const char * country_code
Definition embedder.h:2294
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:1466
const FlutterPlatformMessageResponseHandle * response_handle
Definition embedder.h:1476
const char * channel
Definition embedder.h:1467
const uint8_t * message
Definition embedder.h:1468
size_t layers_count
The count of layers.
Definition embedder.h:2200
FlutterViewId view_id
The identifier of the target view.
Definition embedder.h:2194
const FlutterLayer ** layers
The layers that should be composited onto the view.
Definition embedder.h:2197
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2488
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:1826
size_t node_count
The number of semantics node updates.
Definition embedder.h:1830
size_t custom_action_count
The number of semantics custom action updates.
Definition embedder.h:1834
FlutterSemanticsNode2 ** nodes
Definition embedder.h:1832
FlutterSemanticsCustomAction2 ** custom_actions
Definition embedder.h:1837
FlutterViewId view_id
Definition embedder.h:1839
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:1894
BoolCallback runs_task_on_current_thread_callback
Definition embedder.h:1900
FlutterTaskRunnerPostTaskCallback post_task_callback
Definition embedder.h:1911
FlutterViewId view_id
The identifier of the view that received the focus event.
Definition embedder.h:1257
std::shared_ptr< const fml::Mapping > data
int64_t texture_id
#define BASE_DCHECK(condition)
Definition logging.h:63
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
UINT_PTR WPARAM