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