Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
flutter_windows_engine.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
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
32static constexpr char kAccessibilityChannelName[] = "flutter/accessibility";
33
34namespace flutter {
35
36namespace {
37
38// Lifted from vsync_waiter_fallback.cc
39static 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.
53FlutterRendererConfig 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;
74 [](void* user_data, const FlutterFrameInfo* info) -> uint32_t {
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 };
89 [](void* user_data, int64_t texture_id, size_t width, size_t height,
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.
105FlutterRendererConfig GetSoftwareRendererConfig() {
106 FlutterRendererConfig config = {};
107 config.type = kSoftware;
108 config.software.struct_size = sizeof(config.software);
110 [](void* user_data, const void* allocation, size_t row_bytes,
111 size_t height) {
113 return false;
114 };
115 return config;
116}
117
118// Converts a FlutterPlatformMessage to an equivalent FlutterDesktopMessage.
120 const FlutterPlatformMessage& engine_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.
132FlutterLocale 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
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
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_ =
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) {
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
229FlutterWindowsEngine::~FlutterWindowsEngine() {
230 messenger_->SetEngine(nullptr);
231 Stop();
232}
233
234void FlutterWindowsEngine::SetSwitches(
235 const std::vector<std::string>& switches) {
236 project_->SetSwitches(switches);
237}
238
239bool FlutterWindowsEngine::Run() {
240 return Run("");
241}
242
243bool 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
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_,
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
482bool 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
495std::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 {
518 bool added;
519 };
520 Captures captures = {};
521
522 FlutterWindowMetricsEvent metrics = view->CreateWindowMetricsEvent();
523
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) {
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
566void 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 {
575 bool removed;
576 };
577 Captures captures = {};
578
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
628void 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
637std::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
656FlutterWindowsView* 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.
668FlutterDesktopPluginRegistrarRef FlutterWindowsEngine::GetRegistrar() {
669 return plugin_registrar_.get();
670}
671
672void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
675 plugin_registrar_destruction_callbacks_[callback] = registrar;
676}
677
678void FlutterWindowsEngine::SendWindowMetricsEvent(
680 if (engine_) {
681 embedder_api_.SendWindowMetricsEvent(engine_, &event);
682 }
683}
684
685void FlutterWindowsEngine::SendPointerEvent(const FlutterPointerEvent& event) {
686 if (engine_) {
687 embedder_api_.SendPointerEvent(engine_, &event, 1);
688 }
689}
690
691void FlutterWindowsEngine::SendKeyEvent(const FlutterKeyEvent& event,
693 void* user_data) {
694 if (engine_) {
695 embedder_api_.SendKeyEvent(engine_, &event, callback, user_data);
696 }
697}
698
699bool 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) {
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 = {
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
733void FlutterWindowsEngine::SendPlatformMessageResponse(
735 const uint8_t* data,
736 size_t data_length) {
737 embedder_api_.SendPlatformMessageResponse(engine_, handle, data, data_length);
738}
739
740void 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
754void FlutterWindowsEngine::ReloadSystemFonts() {
755 embedder_api_.ReloadSystemFonts(engine_);
756}
757
758void FlutterWindowsEngine::ScheduleFrame() {
759 embedder_api_.ScheduleFrame(engine_);
760}
761
762void 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.
771 static_cast<FlutterWindowsEngine*>(user_data);
772
773 self->task_runner_->PostTask(std::move(self->next_frame_callback_));
774 },
775 this);
776}
777
778void FlutterWindowsEngine::SetLifecycleState(flutter::AppLifecycleState state) {
779 if (lifecycle_manager_) {
780 lifecycle_manager_->SetLifecycleState(state);
781 }
782}
783
784void 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
802void 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
816std::unique_ptr<KeyboardHandlerBase>
817FlutterWindowsEngine::CreateKeyboardKeyHandler(
818 BinaryMessenger* messenger,
821 auto keyboard_key_handler = std::make_unique<KeyboardKeyHandler>(messenger);
822 keyboard_key_handler->AddDelegate(
823 std::make_unique<KeyboardKeyEmbedderHandler>(
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
835std::unique_ptr<TextInputPlugin> FlutterWindowsEngine::CreateTextInputPlugin(
836 BinaryMessenger* messenger) {
837 return std::make_unique<TextInputPlugin>(messenger, this);
838}
839
840bool FlutterWindowsEngine::RegisterExternalTexture(int64_t texture_id) {
841 return (embedder_api_.RegisterExternalTexture(engine_, texture_id) ==
842 kSuccess);
843}
844
845bool FlutterWindowsEngine::UnregisterExternalTexture(int64_t texture_id) {
846 return (embedder_api_.UnregisterExternalTexture(engine_, texture_id) ==
847 kSuccess);
848}
849
850bool FlutterWindowsEngine::MarkExternalTextureFrameAvailable(
851 int64_t texture_id) {
852 return (embedder_api_.MarkExternalTextureFrameAvailable(
853 engine_, texture_id) == kSuccess);
854}
855
856bool FlutterWindowsEngine::PostRasterThreadTask(fml::closure callback) const {
857 struct Captures {
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
876bool FlutterWindowsEngine::DispatchSemanticsAction(
877 uint64_t target,
879 fml::MallocMapping data) {
880 return (embedder_api_.DispatchSemanticsAction(engine_, target, action,
881 data.GetMapping(),
882 data.GetSize()) == kSuccess);
883}
884
885void 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
897void FlutterWindowsEngine::OnPreEngineRestart() {
898 // Reset the keyboard's state on hot restart.
899 InitializeKeyboard();
900}
901
902std::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
916void FlutterWindowsEngine::UpdateAccessibilityFeatures() {
917 UpdateHighContrastMode();
918}
919
920void FlutterWindowsEngine::UpdateHighContrastMode() {
921 high_contrast_enabled_ = windows_proc_table_->GetHighContrastEnabled();
922
923 SendAccessibilityFeatures();
924 settings_plugin_->UpdateHighContrastMode(high_contrast_enabled_);
925}
926
927void FlutterWindowsEngine::SendAccessibilityFeatures() {
928 int flags = 0;
929
930 if (high_contrast_enabled_) {
931 flags |=
933 }
934
935 embedder_api_.UpdateAccessibilityFeatures(
936 engine_, static_cast<FlutterAccessibilityFeature>(flags));
937}
938
939void 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
946void 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
953void 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
961void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd,
963 lifecycle_manager_->OnWindowStateEvent(hwnd, event);
964}
965
966std::optional<LRESULT> FlutterWindowsEngine::ProcessExternalWindowMessage(
967 HWND hwnd,
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
978void 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
986bool 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
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static float next(float f)
static sk_sp< Effect > Create()
static void SetUp(BinaryMessenger *binary_messenger, AccessibilityPlugin *plugin)
FlutterWindowsEngine(const FlutterProjectBundle &project, std::shared_ptr< WindowsProcTable > windows_proc_table=nullptr)
std::function< SHORT(UINT, bool)> MapVirtualKeyToScanCode
static std::unique_ptr< Manager > Create(bool enable_impeller)
Definition manager.cc:17
static std::shared_ptr< ProcTable > Create()
Definition proc_table.cc:12
A Mapping like NonOwnedMapping, but uses Free as its release proc.
Definition mapping.h:144
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
Definition embedder.cc:3329
@ kOpenGL
Definition embedder.h:80
FlutterAccessibilityFeature
Definition embedder.h:91
@ kFlutterAccessibilityFeatureHighContrast
Request that UI be rendered with darker colors.
Definition embedder.h:105
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2004
FlutterSemanticsAction
Definition embedder.h:113
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1153
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
#define SAFE_ACCESS(pointer, member, default_value)
GLFWwindow * window
Definition main.cc:45
AtkStateType state
FlutterSemanticsFlag flags
if(end==-1)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
FlKeyEvent * event
uint8_t value
GAsyncResult * result
uint32_t * target
static FlutterDesktopMessage ConvertToDesktopMessage(const FlutterPlatformMessage &engine_message)
void(* FlutterDesktopBinaryReply)(const uint8_t *data, size_t data_size, void *user_data)
void(* FlutterDesktopOnPluginRegistrarDestroyed)(FlutterDesktopPluginRegistrarRef)
static constexpr char kAccessibilityChannelName[]
#define FML_LOG(severity)
Definition logging.h:82
#define FML_UNREACHABLE()
Definition logging.h:109
#define FML_DCHECK(condition)
Definition logging.h:103
const char * name
Definition fuchsia.cc:50
static constexpr FlutterViewId kImplicitViewId
Win32Message message
char ** argv
Definition library.h:9
FlTexture * texture
WindowStateEvent
An event representing a change in window state that may update the.
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service host
Definition switches.h:74
std::vector< LanguageInfo > GetPreferredLanguageInfo()
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
int64_t FlutterViewId
std::pair< bool, std::string > GetExecutablePath()
std::function< void()> closure
Definition closure.h:14
Definition ref_ptr.h:256
int32_t height
int32_t width
Point offset
An update to whether a message channel has a listener set or not.
Definition embedder.h:1533
void(* thread_priority_setter)(FlutterThreadPriority)
Definition embedder.h:1602
const FlutterTaskRunnerDescription * platform_task_runner
Definition embedder.h:1594
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
Definition embedder.h:1589
size_t struct_size
This size of this struct. Must be sizeof(FlutterDisplay).
Definition embedder.h:1971
FlutterEngineDisplayId display_id
Definition embedder.h:1973
Function-pointer-based versions of the APIs above.
Definition embedder.h:3317
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineProcs).
Definition embedder.h:3319
FlutterEngineGetCurrentTimeFnPtr GetCurrentTime
Definition embedder.h:3350
FlutterEngineRunTaskFnPtr RunTask
Definition embedder.h:3351
const char * language_code
Definition embedder.h:1939
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:1935
const char * script_code
Definition embedder.h:1949
const char * country_code
Definition embedder.h:1944
ProcResolver gl_proc_resolver
Definition embedder.h:552
size_t struct_size
The size of this struct. Must be sizeof(FlutterOpenGLRendererConfig).
Definition embedder.h:513
TextureFrameCallback gl_external_texture_frame_callback
Definition embedder.h:557
BoolCallback make_resource_current
Definition embedder.h:535
UIntFrameInfoCallback fbo_with_frame_info_callback
Definition embedder.h:565
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1162
const FlutterPlatformMessageResponseHandle * response_handle
Definition embedder.h:1172
const char * channel
Definition embedder.h:1163
const uint8_t * message
Definition embedder.h:1164
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2138
FlutterSoftwareRendererConfig software
Definition embedder.h:828
FlutterOpenGLRendererConfig open_gl
Definition embedder.h:827
FlutterRendererType type
Definition embedder.h:825
A batch of updates to semantics nodes and custom actions.
Definition embedder.h:1502
size_t struct_size
The size of this struct. Must be sizeof(FlutterSoftwareRendererConfig).
Definition embedder.h:816
SoftwareSurfacePresentCallback surface_present_callback
Definition embedder.h:821
size_t struct_size
The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
Definition embedder.h:1564
BoolCallback runs_task_on_current_thread_callback
Definition embedder.h:1570
FlutterTaskRunnerPostTaskCallback post_task_callback
Definition embedder.h:1581
int64_t texture_id
#define BASE_DCHECK(condition)
Definition logging.h:63
#define ERROR(message)
LONG_PTR LRESULT
unsigned int UINT
LONG_PTR LPARAM
UINT_PTR WPARAM