Flutter Engine
The Flutter Engine
flutter_windows_unittests.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/public/flutter_windows.h"
6
7#include <dxgi.h>
8#include <wrl/client.h>
9#include <thread>
10
11#include "flutter/fml/synchronization/count_down_latch.h"
12#include "flutter/fml/synchronization/waitable_event.h"
13#include "flutter/shell/platform/common/app_lifecycle_state.h"
14#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
15#include "flutter/shell/platform/windows/egl/manager.h"
16#include "flutter/shell/platform/windows/testing/engine_modifier.h"
17#include "flutter/shell/platform/windows/testing/windows_test.h"
18#include "flutter/shell/platform/windows/testing/windows_test_config_builder.h"
19#include "flutter/shell/platform/windows/testing/windows_test_context.h"
20#include "flutter/shell/platform/windows/windows_lifecycle_manager.h"
21#include "flutter/testing/stream_capture.h"
22#include "gmock/gmock.h"
23#include "gtest/gtest.h"
25
26namespace flutter {
27namespace testing {
28
29namespace {
30
31// An EGL manager that initializes EGL but fails to create surfaces.
32class HalfBrokenEGLManager : public egl::Manager {
33 public:
34 HalfBrokenEGLManager() : egl::Manager(/*enable_impeller = */ false) {}
35
36 std::unique_ptr<egl::WindowSurface>
37 CreateWindowSurface(HWND hwnd, size_t width, size_t height) override {
38 return nullptr;
39 }
40};
41
42class MockWindowsLifecycleManager : public WindowsLifecycleManager {
43 public:
44 MockWindowsLifecycleManager(FlutterWindowsEngine* engine)
46
48};
49
50// Process the next win32 message if there is one. This can be used to
51// pump the Windows platform thread task runner.
52void PumpMessage() {
53 ::MSG msg;
54 if (::GetMessage(&msg, nullptr, 0, 0)) {
55 ::TranslateMessage(&msg);
57 }
58}
59
60} // namespace
61
62// Verify that we can fetch a texture registrar.
63// Prevent regression: https://github.com/flutter/flutter/issues/86617
64TEST(WindowsNoFixtureTest, GetTextureRegistrar) {
65 FlutterDesktopEngineProperties properties = {};
66 properties.assets_path = L"";
67 properties.icu_data_path = L"icudtl.dat";
68 auto engine = FlutterDesktopEngineCreate(&properties);
69 ASSERT_NE(engine, nullptr);
71 EXPECT_NE(texture_registrar, nullptr);
73}
74
75// Verify we can successfully launch main().
76TEST_F(WindowsTest, LaunchMain) {
77 auto& context = GetContext();
79 ViewControllerPtr controller{builder.Run()};
80 ASSERT_NE(controller, nullptr);
81}
82
83// Verify there is no unexpected output from launching main.
84TEST_F(WindowsTest, LaunchMainHasNoOutput) {
85 // Replace stdout & stderr stream buffers with our own.
86 StreamCapture stdout_capture(&std::cout);
87 StreamCapture stderr_capture(&std::cerr);
88
89 auto& context = GetContext();
91 ViewControllerPtr controller{builder.Run()};
92 ASSERT_NE(controller, nullptr);
93
94 stdout_capture.Stop();
95 stderr_capture.Stop();
96
97 // Verify stdout & stderr have no output.
98 EXPECT_TRUE(stdout_capture.GetOutput().empty());
99 EXPECT_TRUE(stderr_capture.GetOutput().empty());
100}
101
102// Verify we can successfully launch a custom entry point.
103TEST_F(WindowsTest, LaunchCustomEntrypoint) {
104 auto& context = GetContext();
106 builder.SetDartEntrypoint("customEntrypoint");
107 ViewControllerPtr controller{builder.Run()};
108 ASSERT_NE(controller, nullptr);
109}
110
111// Verify that engine launches with the custom entrypoint specified in the
112// FlutterDesktopEngineRun parameter when no entrypoint is specified in
113// FlutterDesktopEngineProperties.dart_entrypoint.
114//
115// TODO(cbracken): https://github.com/flutter/flutter/issues/109285
116TEST_F(WindowsTest, LaunchCustomEntrypointInEngineRunInvocation) {
117 auto& context = GetContext();
119 EnginePtr engine{builder.InitializeEngine()};
120 ASSERT_NE(engine, nullptr);
121
122 ASSERT_TRUE(FlutterDesktopEngineRun(engine.get(), "customEntrypoint"));
123}
124
125// Verify that the engine can launch in headless mode.
126TEST_F(WindowsTest, LaunchHeadlessEngine) {
127 auto& context = GetContext();
129 builder.SetDartEntrypoint("signalViewIds");
130 EnginePtr engine{builder.RunHeadless()};
131 ASSERT_NE(engine, nullptr);
132
133 std::string view_ids;
135 context.AddNativeFunction(
136 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
137 auto handle = Dart_GetNativeArgument(args, 0);
138 ASSERT_FALSE(Dart_IsError(handle));
140 latch.Signal();
141 }));
142
143 ViewControllerPtr controller{builder.Run()};
144 ASSERT_NE(controller, nullptr);
145
146 // Verify a headless app has the implicit view.
147 latch.Wait();
148 EXPECT_EQ(view_ids, "View IDs: [0]");
149}
150
151// Verify that the engine can return to headless mode.
152TEST_F(WindowsTest, EngineCanTransitionToHeadless) {
153 auto& context = GetContext();
155 EnginePtr engine{builder.RunHeadless()};
156 ASSERT_NE(engine, nullptr);
157
158 // Create and then destroy a view controller that does not own its engine.
159 // This causes the engine to transition back to headless mode.
160 {
162 ViewControllerPtr controller{
164
165 ASSERT_NE(controller, nullptr);
166 }
167
168 // The engine is back in headless mode now.
169 ASSERT_NE(engine, nullptr);
170
171 auto engine_ptr = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
172 ASSERT_TRUE(engine_ptr->running());
173}
174
175// Verify that accessibility features are initialized when a view is created.
176TEST_F(WindowsTest, LaunchRefreshesAccessibility) {
177 auto& context = GetContext();
179 EnginePtr engine{builder.InitializeEngine()};
180 EngineModifier modifier{
181 reinterpret_cast<FlutterWindowsEngine*>(engine.get())};
182
183 auto called = false;
184 modifier.embedder_api().UpdateAccessibilityFeatures = MOCK_ENGINE_PROC(
185 UpdateAccessibilityFeatures, ([&called](auto engine, auto flags) {
186 called = true;
187 return kSuccess;
188 }));
189
190 ViewControllerPtr controller{
192
193 ASSERT_TRUE(called);
194}
195
196// Verify that engine fails to launch when a conflicting entrypoint in
197// FlutterDesktopEngineProperties.dart_entrypoint and the
198// FlutterDesktopEngineRun parameter.
199//
200// TODO(cbracken): https://github.com/flutter/flutter/issues/109285
201TEST_F(WindowsTest, LaunchConflictingCustomEntrypoints) {
202 auto& context = GetContext();
204 builder.SetDartEntrypoint("customEntrypoint");
205 EnginePtr engine{builder.InitializeEngine()};
206 ASSERT_NE(engine, nullptr);
207
208 ASSERT_FALSE(FlutterDesktopEngineRun(engine.get(), "conflictingEntrypoint"));
209}
210
211// Verify that native functions can be registered and resolved.
212TEST_F(WindowsTest, VerifyNativeFunction) {
213 auto& context = GetContext();
215 builder.SetDartEntrypoint("verifyNativeFunction");
216
218 auto native_entry =
220 context.AddNativeFunction("Signal", native_entry);
221
222 ViewControllerPtr controller{builder.Run()};
223 ASSERT_NE(controller, nullptr);
224
225 // Wait until signal has been called.
226 latch.Wait();
227}
228
229// Verify that native functions that pass parameters can be registered and
230// resolved.
231TEST_F(WindowsTest, VerifyNativeFunctionWithParameters) {
232 auto& context = GetContext();
234 builder.SetDartEntrypoint("verifyNativeFunctionWithParameters");
235
236 bool bool_value = false;
238 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
239 auto handle = Dart_GetNativeBooleanArgument(args, 0, &bool_value);
240 ASSERT_FALSE(Dart_IsError(handle));
241 latch.Signal();
242 });
243 context.AddNativeFunction("SignalBoolValue", native_entry);
244
245 ViewControllerPtr controller{builder.Run()};
246 ASSERT_NE(controller, nullptr);
247
248 // Wait until signalBoolValue has been called.
249 latch.Wait();
250 EXPECT_TRUE(bool_value);
251}
252
253// Verify that Platform.executable returns the executable name.
254TEST_F(WindowsTest, PlatformExecutable) {
255 auto& context = GetContext();
257 builder.SetDartEntrypoint("readPlatformExecutable");
258
259 std::string executable_name;
261 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
262 auto handle = Dart_GetNativeArgument(args, 0);
263 ASSERT_FALSE(Dart_IsError(handle));
264 executable_name = tonic::DartConverter<std::string>::FromDart(handle);
265 latch.Signal();
266 });
267 context.AddNativeFunction("SignalStringValue", native_entry);
268
269 ViewControllerPtr controller{builder.Run()};
270 ASSERT_NE(controller, nullptr);
271
272 // Wait until signalStringValue has been called.
273 latch.Wait();
274 EXPECT_EQ(executable_name, "flutter_windows_unittests.exe");
275}
276
277// Verify that native functions that return values can be registered and
278// resolved.
279TEST_F(WindowsTest, VerifyNativeFunctionWithReturn) {
280 auto& context = GetContext();
282 builder.SetDartEntrypoint("verifyNativeFunctionWithReturn");
283
284 bool bool_value_to_return = true;
285 fml::CountDownLatch latch(2);
286 auto bool_return_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
287 Dart_SetBooleanReturnValue(args, bool_value_to_return);
288 latch.CountDown();
289 });
290 context.AddNativeFunction("SignalBoolReturn", bool_return_entry);
291
292 bool bool_value_passed = false;
293 auto bool_pass_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
294 auto handle = Dart_GetNativeBooleanArgument(args, 0, &bool_value_passed);
295 ASSERT_FALSE(Dart_IsError(handle));
296 latch.CountDown();
297 });
298 context.AddNativeFunction("SignalBoolValue", bool_pass_entry);
299
300 ViewControllerPtr controller{builder.Run()};
301 ASSERT_NE(controller, nullptr);
302
303 // Wait until signalBoolReturn and signalBoolValue have been called.
304 latch.Wait();
305 EXPECT_TRUE(bool_value_passed);
306}
307
308// Verify the next frame callback is executed.
309TEST_F(WindowsTest, NextFrameCallback) {
310 struct Captures {
311 fml::AutoResetWaitableEvent frame_scheduled_latch;
312 fml::AutoResetWaitableEvent frame_drawn_latch;
313 std::thread::id thread_id;
314 bool done = false;
315 };
316 Captures captures;
317
318 CreateNewThread("test_platform_thread")->PostTask([&]() {
319 captures.thread_id = std::this_thread::get_id();
320
321 auto& context = GetContext();
323 builder.SetDartEntrypoint("drawHelloWorld");
324
325 auto native_entry = CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
326 ASSERT_FALSE(captures.frame_drawn_latch.IsSignaledForTest());
327 captures.frame_scheduled_latch.Signal();
328 });
329 context.AddNativeFunction("NotifyFirstFrameScheduled", native_entry);
330
331 ViewControllerPtr controller{builder.Run()};
332 ASSERT_NE(controller, nullptr);
333
334 auto engine = FlutterDesktopViewControllerGetEngine(controller.get());
335
337 engine,
338 [](void* user_data) {
339 auto captures = static_cast<Captures*>(user_data);
340
341 ASSERT_TRUE(captures->frame_scheduled_latch.IsSignaledForTest());
342
343 // Callback should execute on platform thread.
344 ASSERT_EQ(std::this_thread::get_id(), captures->thread_id);
345
346 // Signal the test passed and end the Windows message loop.
347 captures->done = true;
348 captures->frame_drawn_latch.Signal();
349 },
350 &captures);
351
352 // Pump messages for the Windows platform task runner.
353 while (!captures.done) {
354 PumpMessage();
355 }
356 });
357
358 captures.frame_drawn_latch.Wait();
359}
360
361// Verify the embedder ignores presents to the implicit view when there is no
362// implicit view.
363TEST_F(WindowsTest, PresentHeadless) {
364 auto& context = GetContext();
366 builder.SetDartEntrypoint("renderImplicitView");
367
368 EnginePtr engine{builder.RunHeadless()};
369 ASSERT_NE(engine, nullptr);
370
371 bool done = false;
373 engine.get(),
374 [](void* user_data) {
375 // This executes on the platform thread.
376 auto done = reinterpret_cast<std::atomic<bool>*>(user_data);
377 *done = true;
378 },
379 &done);
380
381 // This app is in headless mode, however, the engine assumes the implicit
382 // view always exists. Send window metrics for the implicit view, causing
383 // the engine to present to the implicit view. The embedder must not crash.
384 auto engine_ptr = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
385 FlutterWindowMetricsEvent metrics = {};
386 metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
387 metrics.width = 100;
388 metrics.height = 100;
389 metrics.pixel_ratio = 1.0;
390 metrics.view_id = kImplicitViewId;
391 engine_ptr->SendWindowMetricsEvent(metrics);
392
393 // Pump messages for the Windows platform task runner.
394 while (!done) {
395 PumpMessage();
396 }
397}
398
399// Implicit view has the implicit view ID.
400TEST_F(WindowsTest, GetViewId) {
401 auto& context = GetContext();
403 ViewControllerPtr controller{builder.Run()};
404 ASSERT_NE(controller, nullptr);
405 FlutterDesktopViewId view_id =
407
408 ASSERT_EQ(view_id, static_cast<FlutterDesktopViewId>(kImplicitViewId));
409}
410
411TEST_F(WindowsTest, GetGraphicsAdapter) {
412 auto& context = GetContext();
414 ViewControllerPtr controller{builder.Run()};
415 ASSERT_NE(controller, nullptr);
416 auto view = FlutterDesktopViewControllerGetView(controller.get());
417
418 Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter;
419 dxgi_adapter = FlutterDesktopViewGetGraphicsAdapter(view);
420 ASSERT_NE(dxgi_adapter, nullptr);
421 DXGI_ADAPTER_DESC desc{};
422 ASSERT_TRUE(SUCCEEDED(dxgi_adapter->GetDesc(&desc)));
423}
424
425// Implicit view has the implicit view ID.
426TEST_F(WindowsTest, PluginRegistrarGetImplicitView) {
427 auto& context = GetContext();
429 ViewControllerPtr controller{builder.Run()};
430 ASSERT_NE(controller, nullptr);
431
436 FlutterDesktopViewRef implicit_view =
438
439 ASSERT_NE(implicit_view, nullptr);
440}
441
442TEST_F(WindowsTest, PluginRegistrarGetView) {
443 auto& context = GetContext();
445 ViewControllerPtr controller{builder.Run()};
446 ASSERT_NE(controller, nullptr);
447
452
453 FlutterDesktopViewId view_id =
457
459 registrar, static_cast<FlutterDesktopViewId>(123));
460
461 ASSERT_NE(view, nullptr);
462 ASSERT_EQ(view_123, nullptr);
463}
464
465TEST_F(WindowsTest, PluginRegistrarGetViewHeadless) {
466 auto& context = GetContext();
468 EnginePtr engine{builder.RunHeadless()};
469 ASSERT_NE(engine, nullptr);
470
473
474 FlutterDesktopViewRef implicit_view =
477 registrar, static_cast<FlutterDesktopViewId>(123));
478
479 ASSERT_EQ(implicit_view, nullptr);
480 ASSERT_EQ(view_123, nullptr);
481}
482
483// Verify the app does not crash if EGL initializes successfully but
484// the rendering surface cannot be created.
485TEST_F(WindowsTest, SurfaceOptional) {
486 auto& context = GetContext();
488 EnginePtr engine{builder.InitializeEngine()};
489 EngineModifier modifier{
490 reinterpret_cast<FlutterWindowsEngine*>(engine.get())};
491
492 auto egl_manager = std::make_unique<HalfBrokenEGLManager>();
493 ASSERT_TRUE(egl_manager->IsValid());
494 modifier.SetEGLManager(std::move(egl_manager));
495
496 ViewControllerPtr controller{
498
499 ASSERT_NE(controller, nullptr);
500}
501
502// Verify the app produces the expected lifecycle events.
503TEST_F(WindowsTest, Lifecycle) {
504 auto& context = GetContext();
506 EnginePtr engine{builder.InitializeEngine()};
507 auto windows_engine = reinterpret_cast<FlutterWindowsEngine*>(engine.get());
508 EngineModifier modifier{windows_engine};
509
510 auto lifecycle_manager =
511 std::make_unique<MockWindowsLifecycleManager>(windows_engine);
512 auto lifecycle_manager_ptr = lifecycle_manager.get();
513 modifier.SetLifecycleManager(std::move(lifecycle_manager));
514
515 EXPECT_CALL(*lifecycle_manager_ptr,
516 SetLifecycleState(AppLifecycleState::kResumed))
517 .WillOnce([lifecycle_manager_ptr](AppLifecycleState state) {
518 lifecycle_manager_ptr->WindowsLifecycleManager::SetLifecycleState(
519 state);
520 });
521
522 EXPECT_CALL(*lifecycle_manager_ptr,
523 SetLifecycleState(AppLifecycleState::kHidden))
524 .WillOnce([lifecycle_manager_ptr](AppLifecycleState state) {
525 lifecycle_manager_ptr->WindowsLifecycleManager::SetLifecycleState(
526 state);
527 });
528
529 // Create a controller. This launches the engine and sets the app lifecycle
530 // to the "resumed" state.
531 ViewControllerPtr controller{
533
535 FlutterDesktopViewControllerGetView(controller.get());
536 ASSERT_NE(view, nullptr);
537
538 HWND hwnd = FlutterDesktopViewGetHWND(view);
539 ASSERT_NE(hwnd, nullptr);
540
541 // Give the window a non-zero size to show it. This does not change the app
542 // lifecycle directly. However, destroying the view will now result in a
543 // "hidden" app lifecycle event.
544 ::MoveWindow(hwnd, /* X */ 0, /* Y */ 0, /* nWidth*/ 100, /* nHeight*/ 100,
545 /* bRepaint*/ false);
546}
547
548TEST_F(WindowsTest, GetKeyboardStateHeadless) {
549 auto& context = GetContext();
551 builder.SetDartEntrypoint("sendGetKeyboardState");
552
553 std::atomic<bool> done = false;
554 context.AddNativeFunction(
555 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
556 auto handle = Dart_GetNativeArgument(args, 0);
557 ASSERT_FALSE(Dart_IsError(handle));
559 EXPECT_EQ(value, "Success");
560 done = true;
561 }));
562
563 ViewControllerPtr controller{builder.Run()};
564 ASSERT_NE(controller, nullptr);
565
566 // Pump messages for the Windows platform task runner.
567 ::MSG msg;
568 while (!done) {
569 PumpMessage();
570 }
571}
572
573// Verify the embedder can add and remove views.
574TEST_F(WindowsTest, AddRemoveView) {
575 std::mutex mutex;
576 std::string view_ids;
577
578 auto& context = GetContext();
580 builder.SetDartEntrypoint("onMetricsChangedSignalViewIds");
581
582 fml::AutoResetWaitableEvent ready_latch;
583 context.AddNativeFunction(
584 "Signal", CREATE_NATIVE_ENTRY(
585 [&](Dart_NativeArguments args) { ready_latch.Signal(); }));
586
587 context.AddNativeFunction(
588 "SignalStringValue", CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) {
589 auto handle = Dart_GetNativeArgument(args, 0);
590 ASSERT_FALSE(Dart_IsError(handle));
591
592 std::scoped_lock lock{mutex};
594 }));
595
596 // Create the implicit view.
597 ViewControllerPtr first_controller{builder.Run()};
598 ASSERT_NE(first_controller, nullptr);
599
600 ready_latch.Wait();
601
602 // Create a second view.
604 FlutterDesktopViewControllerGetEngine(first_controller.get());
606 properties.width = 100;
607 properties.height = 100;
608 ViewControllerPtr second_controller{
610 ASSERT_NE(second_controller, nullptr);
611
612 // Pump messages for the Windows platform task runner until the view is added.
613 while (true) {
614 PumpMessage();
615 std::scoped_lock lock{mutex};
616 if (view_ids == "View IDs: [0, 1]") {
617 break;
618 }
619 }
620
621 // Delete the second view and pump messages for the Windows platform task
622 // runner until the view is removed.
623 second_controller.reset();
624 while (true) {
625 PumpMessage();
626 std::scoped_lock lock{mutex};
627 if (view_ids == "View IDs: [0]") {
628 break;
629 }
630 }
631}
632
633} // namespace testing
634} // namespace flutter
static void done(const char *config, const char *src, const char *srcOptions, const char *name)
Definition: DM.cpp:263
WindowsLifecycleManager(FlutterWindowsEngine *engine)
virtual void SetLifecycleState(AppLifecycleState state)
MOCK_METHOD(void, Quit,(std::optional< HWND >, std::optional< WPARAM >, std::optional< LPARAM >, UINT),(override))
virtual void PostTask(const fml::closure &task) override
Definition: task_runner.cc:24
DART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args, int index)
struct _Dart_NativeArguments * Dart_NativeArguments
Definition: dart_api.h:3019
DART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args, bool retval)
DART_EXPORT bool Dart_IsError(Dart_Handle handle)
DART_EXPORT Dart_Handle Dart_GetNativeBooleanArgument(Dart_NativeArguments args, int index, bool *value)
@ kSuccess
Definition: embedder.h:73
FlutterEngine engine
Definition: main.cc:68
AtkStateType state
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
uint8_t value
FlutterDesktopViewControllerRef FlutterDesktopEngineCreateViewController(FlutterDesktopEngineRef engine, const FlutterDesktopViewControllerProperties *properties)
int64_t FlutterDesktopViewId
struct FlutterDesktopView * FlutterDesktopViewRef
G_BEGIN_DECLS FlTextureRegistrar * texture_registrar
fml::RefPtr< fml::TaskRunner > CreateNewThread(const std::string &name)
SK_API GrDirectContext * GetContext(const SkImage *src)
TEST_F(DisplayListTest, Defaults)
std::unique_ptr< FlutterDesktopEngine, EngineDeleter > EnginePtr
std::unique_ptr< FlutterDesktopViewController, ViewControllerDeleter > ViewControllerPtr
TEST(DisplayListComplexity, EmptyDisplayList)
constexpr int64_t kImplicitViewId
#define MOCK_ENGINE_PROC(proc, mock_impl)
int32_t height
int32_t width
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition: embedder.h:843
size_t height
Physical height of the window.
Definition: embedder.h:847
int64_t view_id
The view that this event is describing.
Definition: embedder.h:865
double pixel_ratio
Scale factor for the physical screen.
Definition: embedder.h:849
size_t width
Physical width of the window.
Definition: embedder.h:845
HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef controller)
FlutterDesktopEngineRef FlutterDesktopEngineCreate(const FlutterDesktopEngineProperties *engine_properties)
FlutterDesktopViewId FlutterDesktopViewControllerGetViewId(FlutterDesktopViewControllerRef controller)
FlutterDesktopPluginRegistrarRef FlutterDesktopEngineGetPluginRegistrar(FlutterDesktopEngineRef engine, const char *plugin_name)
FlutterDesktopViewControllerRef FlutterDesktopViewControllerCreate(int width, int height, FlutterDesktopEngineRef engine)
bool FlutterDesktopEngineDestroy(FlutterDesktopEngineRef engine_ref)
FlutterDesktopEngineRef FlutterDesktopViewControllerGetEngine(FlutterDesktopViewControllerRef controller)
FlutterDesktopViewRef FlutterDesktopViewControllerGetView(FlutterDesktopViewControllerRef controller)
FlutterDesktopTextureRegistrarRef FlutterDesktopEngineGetTextureRegistrar(FlutterDesktopEngineRef engine)
FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(FlutterDesktopPluginRegistrarRef controller)
IDXGIAdapter * FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view)
void FlutterDesktopEngineSetNextFrameCallback(FlutterDesktopEngineRef engine, VoidCallback callback, void *user_data)
FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetViewById(FlutterDesktopPluginRegistrarRef controller, FlutterDesktopViewId view_id)
bool FlutterDesktopEngineRun(FlutterDesktopEngineRef engine, const char *entry_point)
#define CREATE_NATIVE_ENTRY(native_entry)
void * user_data
const uintptr_t id
#define EXPECT_TRUE(handle)
Definition: unit_test.h:678
#define SUCCEEDED(hr)
struct tagMSG MSG
#define GetMessage
#define DispatchMessage