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