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