| Flutter Windows Embedder |

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