Flutter Engine
 
Loading...
Searching...
No Matches
FlutterEngine.mm
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
7
8#include <algorithm>
9#include <iostream>
10#include <sstream>
11#include <vector>
12
17
18#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
20#import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
34
36
37NSString* const kFlutterPlatformChannel = @"flutter/platform";
38NSString* const kFlutterSettingsChannel = @"flutter/settings";
39NSString* const kFlutterLifecycleChannel = @"flutter/lifecycle";
40
42
43/**
44 * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
45 * the returned struct.
46 */
47static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
48 FlutterLocale flutterLocale = {};
49 flutterLocale.struct_size = sizeof(FlutterLocale);
50 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
51 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
52 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
53 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
54 return flutterLocale;
55}
56
57/// The private notification for voice over.
58static NSString* const kEnhancedUserInterfaceNotification =
59 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
60static NSString* const kEnhancedUserInterfaceKey = @"AXEnhancedUserInterface";
61
62/// Clipboard plain text format.
63constexpr char kTextPlainFormat[] = "text/plain";
64
65#pragma mark -
66
67// Records an active handler of the messenger (FlutterEngine) that listens to
68// platform messages on a given channel.
69@interface FlutterEngineHandlerInfo : NSObject
70
71- (instancetype)initWithConnection:(NSNumber*)connection
72 handler:(FlutterBinaryMessageHandler)handler;
73
74@property(nonatomic, readonly) FlutterBinaryMessageHandler handler;
75@property(nonatomic, readonly) NSNumber* connection;
76
77@end
78
79@implementation FlutterEngineHandlerInfo
80- (instancetype)initWithConnection:(NSNumber*)connection
81 handler:(FlutterBinaryMessageHandler)handler {
82 self = [super init];
83 NSAssert(self, @"Super init cannot be nil");
85 _handler = handler;
86 return self;
87}
88@end
89
90#pragma mark -
91
92/**
93 * Private interface declaration for FlutterEngine.
94 */
95@interface FlutterEngine () <FlutterBinaryMessenger,
96 FlutterMouseCursorPluginDelegate,
97 FlutterKeyboardManagerDelegate,
98 FlutterTextInputPluginDelegate>
99
100/**
101 * A mutable array that holds one bool value that determines if responses to platform messages are
102 * clear to execute. This value should be read or written only inside of a synchronized block and
103 * will return `NO` after the FlutterEngine has been dealloc'd.
104 */
105@property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
106
107/**
108 * All delegates added via plugin calls to addApplicationDelegate.
109 */
110@property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
111
112/**
113 * All registrars returned from registrarForPlugin:
114 */
115@property(nonatomic, readonly)
116 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
117
118- (nullable FlutterViewController*)viewControllerForIdentifier:
119 (FlutterViewIdentifier)viewIdentifier;
120
121/**
122 * An internal method that adds the view controller with the given ID.
123 *
124 * This method assigns the controller with the ID, puts the controller into the
125 * map, and does assertions related to the implicit view ID.
126 */
127- (void)registerViewController:(FlutterViewController*)controller
128 forIdentifier:(FlutterViewIdentifier)viewIdentifier;
129
130/**
131 * An internal method that removes the view controller with the given ID.
132 *
133 * This method clears the ID of the controller, removes the controller from the
134 * map. This is an no-op if the view ID is not associated with any view
135 * controllers.
136 */
137- (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier;
138
139/**
140 * Shuts down the engine if view requirement is not met, and headless execution
141 * is not allowed.
142 */
143- (void)shutDownIfNeeded;
144
145/**
146 * Sends the list of user-preferred locales to the Flutter engine.
147 */
148- (void)sendUserLocales;
149
150/**
151 * Handles a platform message from the engine.
152 */
153- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
154
155/**
156 * Invoked right before the engine is restarted.
157 *
158 * This should reset states to as if the application has just started. It
159 * usually indicates a hot restart (Shift-R in Flutter CLI.)
160 */
161- (void)engineCallbackOnPreEngineRestart;
162
163/**
164 * Requests that the task be posted back the to the Flutter engine at the target time. The target
165 * time is in the clock used by the Flutter engine.
166 */
167- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
168
169/**
170 * Loads the AOT snapshots and instructions from the elf bundle (app_elf_snapshot.so) into _aotData,
171 * if it is present in the assets directory.
172 */
173- (void)loadAOTData:(NSString*)assetsDir;
174
175/**
176 * Creates a platform view channel and sets up the method handler.
177 */
178- (void)setUpPlatformViewChannel;
179
180/**
181 * Creates an accessibility channel and sets up the message handler.
182 */
183- (void)setUpAccessibilityChannel;
184
185/**
186 * Handles messages received from the Flutter engine on the _*Channel channels.
187 */
188- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
189
190@end
191
192#pragma mark -
193
195 __weak FlutterEngine* _engine;
197}
198
199- (instancetype)initWithEngine:(FlutterEngine*)engine
200 terminator:(FlutterTerminationCallback)terminator {
201 self = [super init];
202 _acceptingRequests = NO;
203 _engine = engine;
204 _terminator = terminator ? terminator : ^(id sender) {
205 // Default to actually terminating the application. The terminator exists to
206 // allow tests to override it so that an actual exit doesn't occur.
207 [[NSApplication sharedApplication] terminate:sender];
208 };
209 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
210 if ([appDelegate respondsToSelector:@selector(setTerminationHandler:)]) {
211 FlutterAppDelegate* flutterAppDelegate = reinterpret_cast<FlutterAppDelegate*>(appDelegate);
212 flutterAppDelegate.terminationHandler = self;
213 }
214 return self;
215}
216
217// This is called by the method call handler in the engine when the application
218// requests termination itself.
219- (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*, id>*)arguments
220 result:(FlutterResult)result {
221 NSString* type = arguments[@"type"];
222 // Ignore the "exitCode" value in the arguments because AppKit doesn't have
223 // any good way to set the process exit code other than calling exit(), and
224 // that bypasses all of the native applicationShouldExit shutdown events,
225 // etc., which we don't want to skip.
226
227 FlutterAppExitType exitType =
228 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
229
230 [self requestApplicationTermination:[NSApplication sharedApplication]
231 exitType:exitType
232 result:result];
233}
234
235// This is called by the FlutterAppDelegate whenever any termination request is
236// received.
237- (void)requestApplicationTermination:(id)sender
238 exitType:(FlutterAppExitType)type
239 result:(nullable FlutterResult)result {
240 _shouldTerminate = YES;
241 if (![self acceptingRequests]) {
242 // Until the Dart application has signaled that it is ready to handle
243 // termination requests, the app will just terminate when asked.
244 type = kFlutterAppExitTypeRequired;
245 }
246 switch (type) {
247 case kFlutterAppExitTypeCancelable: {
248 FlutterJSONMethodCodec* codec = [FlutterJSONMethodCodec sharedInstance];
249 FlutterMethodCall* methodCall =
250 [FlutterMethodCall methodCallWithMethodName:@"System.requestAppExit" arguments:nil];
251 [_engine sendOnChannel:kFlutterPlatformChannel
252 message:[codec encodeMethodCall:methodCall]
253 binaryReply:^(NSData* _Nullable reply) {
254 NSAssert(_terminator, @"terminator shouldn't be nil");
255 id decoded_reply = [codec decodeEnvelope:reply];
256 if ([decoded_reply isKindOfClass:[FlutterError class]]) {
257 FlutterError* error = (FlutterError*)decoded_reply;
258 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
259 [error details]);
260 _terminator(sender);
261 return;
262 }
263 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
264 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
265 decoded_reply);
266 _terminator(sender);
267 return;
268 }
269 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
270 if ([replyArgs[@"response"] isEqual:@"exit"]) {
271 _terminator(sender);
272 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
273 _shouldTerminate = NO;
274 }
275 if (result != nil) {
276 result(replyArgs);
277 }
278 }];
279 break;
280 }
281 case kFlutterAppExitTypeRequired:
282 NSAssert(_terminator, @"terminator shouldn't be nil");
283 _terminator(sender);
284 break;
285 }
286}
287
288@end
289
290#pragma mark -
291
292@implementation FlutterPasteboard
293
294- (NSInteger)clearContents {
295 return [[NSPasteboard generalPasteboard] clearContents];
296}
297
298- (NSString*)stringForType:(NSPasteboardType)dataType {
299 return [[NSPasteboard generalPasteboard] stringForType:dataType];
300}
301
302- (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
303 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
304}
305
306@end
307
308#pragma mark -
309
310/**
311 * `FlutterPluginRegistrar` implementation handling a single plugin.
312 */
314- (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
315 flutterEngine:(nonnull FlutterEngine*)flutterEngine;
316
317- (nullable NSView*)viewForIdentifier:(FlutterViewIdentifier)viewIdentifier;
318
319/**
320 * The value published by this plugin, or NSNull if nothing has been published.
321 *
322 * The unusual NSNull is for the documented behavior of valuePublishedByPlugin:.
323 */
324@property(nonatomic, readonly, nonnull) NSObject* publishedValue;
325@end
326
327@implementation FlutterEngineRegistrar {
328 NSString* _pluginKey;
330}
331
332@dynamic view;
333
334- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
335 self = [super init];
336 if (self) {
337 _pluginKey = [pluginKey copy];
338 _flutterEngine = flutterEngine;
339 _publishedValue = [NSNull null];
340 }
341 return self;
342}
343
344#pragma mark - FlutterPluginRegistrar
345
346- (id<FlutterBinaryMessenger>)messenger {
348}
349
350- (id<FlutterTextureRegistry>)textures {
351 return _flutterEngine.renderer;
352}
353
354- (NSView*)view {
355 return [self viewForIdentifier:kFlutterImplicitViewId];
356}
357
358- (NSView*)viewForIdentifier:(FlutterViewIdentifier)viewIdentifier {
359 FlutterViewController* controller = [_flutterEngine viewControllerForIdentifier:viewIdentifier];
360 if (controller == nil) {
361 return nil;
362 }
363 if (!controller.viewLoaded) {
364 [controller loadView];
365 }
366 return controller.flutterView;
367}
368
369- (NSViewController*)viewController {
370 return [_flutterEngine viewControllerForIdentifier:kFlutterImplicitViewId];
371}
372
373- (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
374 channel:(nonnull FlutterMethodChannel*)channel {
375 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
376 [delegate handleMethodCall:call result:result];
377 }];
378}
379
380- (void)addApplicationDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
381 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
382 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
383 id<FlutterAppLifecycleProvider> lifeCycleProvider =
384 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
385 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
386 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
387 }
388}
389
390- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
391 withId:(nonnull NSString*)factoryId {
392 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
393}
394
395- (void)publish:(NSObject*)value {
396 _publishedValue = value;
397}
398
399- (NSString*)lookupKeyForAsset:(NSString*)asset {
401}
402
403- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
404 return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
405}
406
407@end
408
409// Callbacks provided to the engine. See the called methods for documentation.
410#pragma mark - Static methods provided to engine configuration
411
414 [engine engineCallbackOnPlatformMessage:message];
415}
416
417#pragma mark -
418
419@implementation FlutterEngine {
420 // The embedding-API-level engine object.
422
423 // The project being run by this engine.
425
426 // A mapping of channel names to the registered information for those channels.
427 NSMutableDictionary<NSString*, FlutterEngineHandlerInfo*>* _messengerHandlers;
428
429 // A self-incremental integer to assign to newly assigned channels as
430 // identification.
432
433 // Whether the engine can continue running after the view controller is removed.
435
436 // Pointer to the Dart AOT snapshot and instruction data.
438
439 // _macOSCompositor is created when the engine is created and its destruction is handled by ARC
440 // when the engine is destroyed.
441 std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
442
443 // The information of all views attached to this engine mapped from IDs.
444 //
445 // It can't use NSDictionary, because the values need to be weak references.
446 NSMapTable* _viewControllers;
447
448 // FlutterCompositor is copied and used in embedder.cc.
450
451 // Method channel for platform view functions. These functions include creating, disposing and
452 // mutating a platform view.
454
455 // Used to support creation and deletion of platform views and registering platform view
456 // factories. Lifecycle is tied to the engine.
458
459 // Used to manage Flutter windows created by the Dart application
461
462 // A message channel for sending user settings to the flutter engine.
464
465 // A message channel for accessibility.
467
468 // A method channel for miscellaneous platform functionality.
470
471 // Whether the application is currently the active application.
473
474 // Whether any portion of the application is currently visible.
476
477 // Proxy to allow plugins, channels to hold a weak reference to the binary messenger (self).
479
480 // Map from ViewId to vsync waiter. Note that this is modified on main thread
481 // but accessed on UI thread, so access must be @synchronized.
482 NSMapTable<NSNumber*, FlutterVSyncWaiter*>* _vsyncWaiters;
483
484 // Weak reference to last view that received a pointer event. This is used to
485 // pair cursor change with a view.
487
488 // Pointer to a keyboard manager.
490
491 // The text input plugin that handles text editing state for text fields.
493
494 // Whether the engine is running in multi-window mode. This affects behavior
495 // when adding view controller (it will fail when calling multiple times without
496 // _multiviewEnabled).
498
499 // View identifier for the next view to be created.
500 // Only used when multiview is enabled.
502}
503
504@synthesize windowController = _windowController;
505
506- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
507 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
508}
509
510static const int kMainThreadPriority = 47;
511
512static void SetThreadPriority(FlutterThreadPriority priority) {
513 if (priority == kDisplay || priority == kRaster) {
514 pthread_t thread = pthread_self();
515 sched_param param;
516 int policy;
517 if (!pthread_getschedparam(thread, &policy, &param)) {
518 param.sched_priority = kMainThreadPriority;
519 pthread_setschedparam(thread, policy, &param);
520 }
521 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
522 }
523}
524
525- (instancetype)initWithName:(NSString*)labelPrefix
526 project:(FlutterDartProject*)project
527 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
528 self = [super init];
529 NSAssert(self, @"Super init cannot be nil");
530
531 [FlutterRunLoop ensureMainLoopInitialized];
532
533 _pasteboard = [[FlutterPasteboard alloc] init];
534 _active = NO;
535 _visible = NO;
536 _project = project ?: [[FlutterDartProject alloc] init];
537 _messengerHandlers = [[NSMutableDictionary alloc] init];
538 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
539 _pluginRegistrars = [[NSMutableDictionary alloc] init];
541 _allowHeadlessExecution = allowHeadlessExecution;
542 _semanticsEnabled = NO;
543 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
544 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
545 [_isResponseValid addObject:@YES];
546 _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self];
547 _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
550
551 _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
552 FlutterEngineGetProcAddresses(&_embedderAPI);
553
554 _viewControllers = [NSMapTable weakToWeakObjectsMapTable];
555 _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self];
556
557 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
558 [notificationCenter addObserver:self
559 selector:@selector(sendUserLocales)
560 name:NSCurrentLocaleDidChangeNotification
561 object:nil];
562
564 // The macOS compositor must be initialized in the initializer because it is
565 // used when adding views, which might happen before runWithEntrypoint.
566 _macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
567 [[FlutterViewEngineProvider alloc] initWithEngine:self],
568 [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController);
569
570 [self setUpPlatformViewChannel];
571
574
575 [self setUpAccessibilityChannel];
576 [self setUpNotificationCenterListeners];
577 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
578 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
579 _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self
580 terminator:nil];
581 id<FlutterAppLifecycleProvider> lifecycleProvider =
582 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
583 [lifecycleProvider addApplicationLifecycleDelegate:self];
584 } else {
585 _terminationHandler = nil;
586 }
587
588 _vsyncWaiters = [NSMapTable strongToStrongObjectsMapTable];
589
590 return self;
591}
592
593- (void)dealloc {
594 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
595 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
596 id<FlutterAppLifecycleProvider> lifecycleProvider =
597 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
598 [lifecycleProvider removeApplicationLifecycleDelegate:self];
599
600 // Unregister any plugins that registered as app delegates, since they are not guaranteed to
601 // live after the engine is destroyed, and their delegation registration is intended to be bound
602 // to the engine and its lifetime.
603 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
604 if (delegate) {
605 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
606 }
607 }
608 }
609 // Clear any published values, just in case a plugin has created a retain cycle with the
610 // registrar.
611 for (NSString* pluginName in _pluginRegistrars) {
612 [_pluginRegistrars[pluginName] publish:[NSNull null]];
613 }
614 @synchronized(_isResponseValid) {
615 [_isResponseValid removeAllObjects];
616 [_isResponseValid addObject:@NO];
617 }
618 [self shutDownEngine];
619 if (_aotData) {
620 _embedderAPI.CollectAOTData(_aotData);
621 }
622}
623
624- (FlutterTaskRunnerDescription)createPlatformThreadTaskDescription {
625 static size_t sTaskRunnerIdentifiers = 0;
626 FlutterTaskRunnerDescription cocoa_task_runner_description = {
628 // Retain for use in post_task_callback. Released in destruction_callback.
629 .user_data = (__bridge_retained void*)self,
630 .runs_task_on_current_thread_callback = [](void* user_data) -> bool {
631 return [[NSThread currentThread] isMainThread];
632 },
633 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
634 void* user_data) -> void {
636 [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
637 },
638 .identifier = ++sTaskRunnerIdentifiers,
639 .destruction_callback =
640 [](void* user_data) {
641 // Balancing release for the retain when setting user_data above.
642 FlutterEngine* engine = (__bridge_transfer FlutterEngine*)user_data;
643 engine = nil;
644 },
645 };
646 return cocoa_task_runner_description;
647}
648
649- (void)onFocusChangeRequest:(const FlutterViewFocusChangeRequest*)request {
650 FlutterViewController* controller = [self viewControllerForIdentifier:request->view_id];
651 if (controller == nil) {
652 return;
653 }
654 if (request->state == kFocused) {
655 [controller.flutterView.window makeFirstResponder:controller.flutterView];
656 }
657}
658
659- (BOOL)runWithEntrypoint:(NSString*)entrypoint {
660 if (self.running) {
661 return NO;
662 }
663
664 if (!_allowHeadlessExecution && [_viewControllers count] == 0) {
665 NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
666 return NO;
667 }
668
669 [self addInternalPlugins];
670
671 // The first argument of argv is required to be the executable name.
672 std::vector<const char*> argv = {[self.executableName UTF8String]};
673 std::vector<std::string> switches = self.switches;
674
675 // Enable Impeller only if specifically asked for from the project or cmdline arguments.
677 std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end()) {
678 switches.push_back("--enable-impeller=true");
679 }
680
682 std::find(switches.begin(), switches.end(), "--enable-flutter-gpu=true") != switches.end()) {
683 switches.push_back("--enable-flutter-gpu=true");
684 }
685
686 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
687 [](const std::string& arg) -> const char* { return arg.c_str(); });
688
689 std::vector<const char*> dartEntrypointArgs;
690 for (NSString* argument in [_project dartEntrypointArguments]) {
691 dartEntrypointArgs.push_back([argument UTF8String]);
692 }
693
694 FlutterProjectArgs flutterArguments = {};
695 flutterArguments.struct_size = sizeof(FlutterProjectArgs);
696 flutterArguments.assets_path = _project.assetsPath.UTF8String;
697 flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String;
698 flutterArguments.command_line_argc = static_cast<int>(argv.size());
699 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
701 flutterArguments.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
702 void* user_data) {
703 // TODO(dkwingsmt): This callback only supports single-view, therefore it
704 // only operates on the implicit view. To support multi-view, we need a
705 // way to pass in the ID (probably through FlutterSemanticsUpdate).
707 [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
708 };
709 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
710 flutterArguments.shutdown_dart_vm_when_done = true;
711 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
712 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
714 flutterArguments.log_message_callback = [](const char* tag, const char* message,
715 void* user_data) {
716 std::stringstream stream;
717 if (tag && tag[0]) {
718 stream << tag << ": ";
719 }
720 stream << message;
721 std::string log = stream.str();
722 [FlutterLogger logDirect:[NSString stringWithUTF8String:log.c_str()]];
723 };
724
725 flutterArguments.engine_id = reinterpret_cast<int64_t>((__bridge void*)self);
726
727 BOOL mergedPlatformUIThread = YES;
728 NSNumber* enableMergedPlatformUIThread =
729 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
730 if (enableMergedPlatformUIThread != nil) {
731 mergedPlatformUIThread = enableMergedPlatformUIThread.boolValue;
732 }
733
734 if (mergedPlatformUIThread) {
735 NSLog(@"Running with merged UI and platform thread. Experimental.");
736 }
737
738 // The task description needs to be created separately for platform task
739 // runner and UI task runner because each one has their own __bridge_retained
740 // engine user data.
741 FlutterTaskRunnerDescription platformTaskRunnerDescription =
742 [self createPlatformThreadTaskDescription];
743 std::optional<FlutterTaskRunnerDescription> uiTaskRunnerDescription;
744 if (mergedPlatformUIThread) {
745 uiTaskRunnerDescription = [self createPlatformThreadTaskDescription];
746 }
747
748 const FlutterCustomTaskRunners custom_task_runners = {
750 .platform_task_runner = &platformTaskRunnerDescription,
751 .thread_priority_setter = SetThreadPriority,
752 .ui_task_runner = uiTaskRunnerDescription ? &uiTaskRunnerDescription.value() : nullptr,
753 };
754 flutterArguments.custom_task_runners = &custom_task_runners;
755
756 [self loadAOTData:_project.assetsPath];
757 if (_aotData) {
758 flutterArguments.aot_data = _aotData;
759 }
760
761 flutterArguments.compositor = [self createFlutterCompositor];
762
763 flutterArguments.on_pre_engine_restart_callback = [](void* user_data) {
765 [engine engineCallbackOnPreEngineRestart];
766 };
767
768 flutterArguments.vsync_callback = [](void* user_data, intptr_t baton) {
770 [engine onVSync:baton];
771 };
772
773 flutterArguments.view_focus_change_request_callback =
774 [](const FlutterViewFocusChangeRequest* request, void* user_data) {
776 [engine onFocusChangeRequest:request];
777 };
778
779 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
780 FlutterEngineResult result = _embedderAPI.Initialize(
781 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
782 if (result != kSuccess) {
783 NSLog(@"Failed to initialize Flutter engine: error %d", result);
784 return NO;
785 }
786
787 result = _embedderAPI.RunInitialized(_engine);
788 if (result != kSuccess) {
789 NSLog(@"Failed to run an initialized engine: error %d", result);
790 return NO;
791 }
792
793 [self sendUserLocales];
794
795 // Update window metric for all view controllers.
796 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
797 FlutterViewController* nextViewController;
798 while ((nextViewController = [viewControllerEnumerator nextObject])) {
799 [self updateWindowMetricsForViewController:nextViewController];
800 }
801
802 [self updateDisplayConfig];
803 // Send the initial user settings such as brightness and text scale factor
804 // to the engine.
805 [self sendInitialSettings];
806 return YES;
807}
808
809- (void)loadAOTData:(NSString*)assetsDir {
810 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
811 return;
812 }
813
814 BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath.
815 NSFileManager* fileManager = [NSFileManager defaultManager];
816
817 // This is the location where the test fixture places the snapshot file.
818 // For applications built by Flutter tool, this is in "App.framework".
819 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
820
821 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
822 return;
823 }
824
825 FlutterEngineAOTDataSource source = {};
827 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
828
829 auto result = _embedderAPI.CreateAOTData(&source, &_aotData);
830 if (result != kSuccess) {
831 NSLog(@"Failed to load AOT data from: %@", elfPath);
832 }
833}
834
835- (void)registerViewController:(FlutterViewController*)controller
836 forIdentifier:(FlutterViewIdentifier)viewIdentifier {
837 _macOSCompositor->AddView(viewIdentifier);
838 NSAssert(controller != nil, @"The controller must not be nil.");
839 if (!_multiViewEnabled) {
840 NSAssert(controller.engine == nil,
841 @"The FlutterViewController is unexpectedly attached to "
842 @"engine %@ before initialization.",
843 controller.engine);
844 }
845 NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil,
846 @"The requested view ID is occupied.");
847 [_viewControllers setObject:controller forKey:@(viewIdentifier)];
848 [controller setUpWithEngine:self viewIdentifier:viewIdentifier];
849 NSAssert(controller.viewIdentifier == viewIdentifier, @"Failed to assign view ID.");
850 // Verify that the controller's property are updated accordingly. Failing the
851 // assertions is likely because either the FlutterViewController or the
852 // FlutterEngine is mocked. Please subclass these classes instead.
853 NSAssert(controller.attached, @"The FlutterViewController should switch to the attached mode "
854 @"after it is added to a FlutterEngine.");
855 NSAssert(controller.engine == self,
856 @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
857 self, controller.engine);
858
859 if (controller.viewLoaded) {
860 [self viewControllerViewDidLoad:controller];
861 }
862
863 if (viewIdentifier != kFlutterImplicitViewId) {
864 // These will be overriden immediately after the FlutterView is created
865 // by actual values.
868 .width = 0,
869 .height = 0,
870 .pixel_ratio = 1.0,
871 };
872 bool added = false;
874 .view_id = viewIdentifier,
875 .view_metrics = &metrics,
876 .user_data = &added,
877 .add_view_callback = [](const FlutterAddViewResult* r) {
878 auto added = reinterpret_cast<bool*>(r->user_data);
879 *added = true;
880 }};
881 // The callback should be called synchronously from platform thread.
882 _embedderAPI.AddView(_engine, &info);
883 FML_DCHECK(added);
884 if (!added) {
885 NSLog(@"Failed to add view with ID %llu", viewIdentifier);
886 }
887 }
888}
889
890- (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
891 __weak FlutterEngine* weakSelf = self;
892 FlutterTimeConverter* timeConverter = [[FlutterTimeConverter alloc] initWithEngine:self];
893 FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
894 initWithDisplayLink:[FlutterDisplayLink displayLinkWithView:viewController.view]
895 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
896 uintptr_t baton) {
897 uint64_t timeNanos = [timeConverter CAMediaTimeToEngineTime:timestamp];
898 uint64_t targetTimeNanos =
899 [timeConverter CAMediaTimeToEngineTime:targetTimestamp];
900 FlutterEngine* engine = weakSelf;
901 if (engine) {
902 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
903 }
904 }];
905 @synchronized(_vsyncWaiters) {
906 FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil);
907 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
908 }
909}
910
911- (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
912 if (viewIdentifier != kFlutterImplicitViewId) {
913 bool removed = false;
915 info.struct_size = sizeof(FlutterRemoveViewInfo);
916 info.view_id = viewIdentifier;
917 info.user_data = &removed;
918 // RemoveViewCallback is not finished synchronously, the remove_view_callback
919 // is called from raster thread when the engine knows for sure that the resources
920 // associated with the view are no longer needed.
922 auto removed = reinterpret_cast<bool*>(r->user_data);
923 [FlutterRunLoop.mainRunLoop performBlock:^{
924 *removed = true;
925 }];
926 };
927 _embedderAPI.RemoveView(_engine, &info);
928 while (!removed) {
929 [[FlutterRunLoop mainRunLoop] pollFlutterMessagesOnce];
930 }
931 }
932
933 _macOSCompositor->RemoveView(viewIdentifier);
934
935 FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier];
936 // The controller can be nil. The engine stores only a weak ref, and this
937 // method could have been called from the controller's dealloc.
938 if (controller != nil) {
939 [controller detachFromEngine];
940 NSAssert(!controller.attached,
941 @"The FlutterViewController unexpectedly stays attached after being removed. "
942 @"In unit tests, this is likely because either the FlutterViewController or "
943 @"the FlutterEngine is mocked. Please subclass these classes instead.");
944 }
945 [_viewControllers removeObjectForKey:@(viewIdentifier)];
946
947 FlutterVSyncWaiter* waiter = nil;
948 @synchronized(_vsyncWaiters) {
949 waiter = [_vsyncWaiters objectForKey:@(viewIdentifier)];
950 [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
951 }
952 [waiter invalidate];
953}
954
955- (void)shutDownIfNeeded {
956 if ([_viewControllers count] == 0 && !_allowHeadlessExecution) {
957 [self shutDownEngine];
958 }
959}
960
961- (FlutterViewController*)viewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
962 FlutterViewController* controller = [_viewControllers objectForKey:@(viewIdentifier)];
963 NSAssert(controller == nil || controller.viewIdentifier == viewIdentifier,
964 @"The stored controller has unexpected view ID.");
965 return controller;
966}
967
968- (void)setViewController:(FlutterViewController*)controller {
969 FlutterViewController* currentController =
970 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
971 if (currentController == controller) {
972 // From nil to nil, or from non-nil to the same controller.
973 return;
974 }
975 if (currentController == nil && controller != nil) {
976 // From nil to non-nil.
977 NSAssert(controller.engine == nil,
978 @"Failed to set view controller to the engine: "
979 @"The given FlutterViewController is already attached to an engine %@. "
980 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
981 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
982 controller.engine);
983 [self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
984 } else if (currentController != nil && controller == nil) {
985 NSAssert(currentController.viewIdentifier == kFlutterImplicitViewId,
986 @"The default controller has an unexpected ID %llu", currentController.viewIdentifier);
987 // From non-nil to nil.
988 [self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
989 [self shutDownIfNeeded];
990 } else {
991 // From non-nil to a different non-nil view controller.
992 NSAssert(NO,
993 @"Failed to set view controller to the engine: "
994 @"The engine already has an implicit view controller %@. "
995 @"If you wanted to make the implicit view render in a different window, "
996 @"you should attach the current view controller to the window instead.",
997 [_viewControllers objectForKey:@(kFlutterImplicitViewId)]);
998 }
999}
1000
1002 return [self viewControllerForIdentifier:kFlutterImplicitViewId];
1003}
1004
1005- (FlutterCompositor*)createFlutterCompositor {
1006 _compositor = {};
1009
1011 FlutterBackingStore* backing_store_out, //
1012 void* user_data //
1013 ) {
1014 return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
1015 config, backing_store_out);
1016 };
1017
1019 void* user_data //
1020 ) { return true; };
1021
1023 return reinterpret_cast<flutter::FlutterCompositor*>(info->user_data)
1024 ->Present(info->view_id, info->layers, info->layers_count);
1025 };
1026
1028
1029 return &_compositor;
1030}
1031
1032- (id<FlutterBinaryMessenger>)binaryMessenger {
1033 return _binaryMessenger;
1034}
1035
1036#pragma mark - Framework-internal methods
1037
1038- (void)addViewController:(FlutterViewController*)controller {
1039 if (!_multiViewEnabled) {
1040 // When multiview is disabled, the engine will only assign views to the implicit view ID.
1041 // The implicit view ID can be reused if and only if the implicit view is unassigned.
1042 NSAssert(self.viewController == nil,
1043 @"The engine already has a view controller for the implicit view.");
1044 self.viewController = controller;
1045 } else {
1046 // When multiview is enabled, the engine will assign views to a self-incrementing ID.
1047 // The implicit view ID can not be reused.
1048 FlutterViewIdentifier viewIdentifier = _nextViewIdentifier++;
1049 [self registerViewController:controller forIdentifier:viewIdentifier];
1050 }
1051}
1052
1053- (void)enableMultiView {
1054 if (!_multiViewEnabled) {
1055 NSAssert(self.viewController == nil,
1056 @"Multiview can only be enabled before adding any view controllers.");
1057 _multiViewEnabled = YES;
1058 }
1059}
1060
1061- (void)windowDidBecomeKey:(FlutterViewIdentifier)viewIdentifier {
1064 .view_id = viewIdentifier,
1065 .state = kFocused,
1066 .direction = kUndefined,
1067 };
1068 _embedderAPI.SendViewFocusEvent(_engine, &event);
1069}
1070
1071- (void)windowDidResignKey:(FlutterViewIdentifier)viewIdentifier {
1074 .view_id = viewIdentifier,
1075 .state = kUnfocused,
1076 .direction = kUndefined,
1077 };
1078 _embedderAPI.SendViewFocusEvent(_engine, &event);
1079}
1080
1081- (void)removeViewController:(nonnull FlutterViewController*)viewController {
1082 [self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
1083 [self shutDownIfNeeded];
1084}
1085
1086- (BOOL)running {
1087 return _engine != nullptr;
1088}
1089
1090- (void)updateDisplayConfig:(NSNotification*)notification {
1091 [self updateDisplayConfig];
1092}
1093
1094- (NSArray<NSScreen*>*)screens {
1095 return [NSScreen screens];
1096}
1097
1098- (void)updateDisplayConfig {
1099 if (!_engine) {
1100 return;
1101 }
1102
1103 std::vector<FlutterEngineDisplay> displays;
1104 for (NSScreen* screen : [self screens]) {
1105 CGDirectDisplayID displayID =
1106 static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
1107
1108 double devicePixelRatio = screen.backingScaleFactor;
1109 FlutterEngineDisplay display;
1110 display.struct_size = sizeof(display);
1111 display.display_id = displayID;
1112 display.single_display = false;
1113 display.width = static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
1114 display.height = static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
1115 display.device_pixel_ratio = devicePixelRatio;
1116
1117 CVDisplayLinkRef displayLinkRef = nil;
1118 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
1119
1120 if (error == 0) {
1121 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
1122 if (!(nominal.flags & kCVTimeIsIndefinite)) {
1123 double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
1124 display.refresh_rate = round(refreshRate);
1125 }
1126 CVDisplayLinkRelease(displayLinkRef);
1127 } else {
1128 display.refresh_rate = 0;
1129 }
1130
1131 displays.push_back(display);
1132 }
1133 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
1134 displays.data(), displays.size());
1135}
1136
1137- (void)onSettingsChanged:(NSNotification*)notification {
1138 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
1139 NSString* brightness =
1140 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
1141 [_settingsChannel sendMessage:@{
1142 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
1143 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
1144 @"textScaleFactor" : @1.0,
1145 @"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]),
1146 }];
1147}
1148
1149- (void)sendInitialSettings {
1150 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
1151 [[NSDistributedNotificationCenter defaultCenter]
1152 addObserver:self
1153 selector:@selector(onSettingsChanged:)
1154 name:@"AppleInterfaceThemeChangedNotification"
1155 object:nil];
1156 [self onSettingsChanged:nil];
1157}
1158
1159- (FlutterEngineProcTable&)embedderAPI {
1160 return _embedderAPI;
1161}
1162
1163- (nonnull NSString*)executableName {
1164 return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter";
1165}
1166
1167- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
1168 if (!_engine || !viewController || !viewController.viewLoaded) {
1169 return;
1170 }
1171 NSAssert([self viewControllerForIdentifier:viewController.viewIdentifier] == viewController,
1172 @"The provided view controller is not attached to this engine.");
1173 NSView* view = viewController.flutterView;
1174 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1175 CGSize scaledSize = scaledBounds.size;
1176 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
1177 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1178 const FlutterWindowMetricsEvent windowMetricsEvent = {
1179 .struct_size = sizeof(windowMetricsEvent),
1180 .width = static_cast<size_t>(scaledSize.width),
1181 .height = static_cast<size_t>(scaledSize.height),
1182 .pixel_ratio = pixelRatio,
1183 .left = static_cast<size_t>(scaledBounds.origin.x),
1184 .top = static_cast<size_t>(scaledBounds.origin.y),
1185 .display_id = static_cast<uint64_t>(displayId),
1186 .view_id = viewController.viewIdentifier,
1187 };
1188 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1189}
1190
1191- (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1192 _embedderAPI.SendPointerEvent(_engine, &event, 1);
1193 _lastViewWithPointerEvent = [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView;
1194}
1195
1196- (void)setSemanticsEnabled:(BOOL)enabled {
1197 if (_semanticsEnabled == enabled) {
1198 return;
1199 }
1200 _semanticsEnabled = enabled;
1201
1202 // Update all view controllers' bridges.
1203 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1204 FlutterViewController* nextViewController;
1205 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1206 [nextViewController notifySemanticsEnabledChanged];
1207 }
1208
1209 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1210}
1211
1212- (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1213 toTarget:(uint16_t)target
1214 withData:(fml::MallocMapping)data {
1215 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1216}
1217
1218- (FlutterPlatformViewController*)platformViewController {
1220}
1221
1222#pragma mark - Private methods
1223
1224- (void)sendUserLocales {
1225 if (!self.running) {
1226 return;
1227 }
1228
1229 // Create a list of FlutterLocales corresponding to the preferred languages.
1230 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1231 std::vector<FlutterLocale> flutterLocales;
1232 flutterLocales.reserve(locales.count);
1233 for (NSString* localeID in [NSLocale preferredLanguages]) {
1234 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1235 [locales addObject:locale];
1236 flutterLocales.push_back(FlutterLocaleFromNSLocale(locale));
1237 }
1238 // Convert to a list of pointers, and send to the engine.
1239 std::vector<const FlutterLocale*> flutterLocaleList;
1240 flutterLocaleList.reserve(flutterLocales.size());
1241 std::transform(flutterLocales.begin(), flutterLocales.end(),
1242 std::back_inserter(flutterLocaleList),
1243 [](const auto& arg) -> const auto* { return &arg; });
1244 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1245}
1246
1247- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1248 NSData* messageData = nil;
1249 if (message->message_size > 0) {
1250 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1251 length:message->message_size
1252 freeWhenDone:NO];
1253 }
1254 NSString* channel = @(message->channel);
1255 __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1256 __block FlutterEngine* weakSelf = self;
1257 NSMutableArray* isResponseValid = self.isResponseValid;
1258 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1259 _embedderAPI.SendPlatformMessageResponse;
1260 FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
1261 @synchronized(isResponseValid) {
1262 if (![isResponseValid[0] boolValue]) {
1263 // Ignore, engine was killed.
1264 return;
1265 }
1266 if (responseHandle) {
1267 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1268 static_cast<const uint8_t*>(response.bytes), response.length);
1269 responseHandle = NULL;
1270 } else {
1271 NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
1272 "on channel '%@'.",
1273 channel);
1274 }
1275 }
1276 };
1277
1278 FlutterEngineHandlerInfo* handlerInfo = _messengerHandlers[channel];
1279 if (handlerInfo) {
1280 handlerInfo.handler(messageData, binaryResponseHandler);
1281 } else {
1282 binaryResponseHandler(nil);
1283 }
1284}
1285
1286- (void)engineCallbackOnPreEngineRestart {
1287 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1288 FlutterViewController* nextViewController;
1289 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1290 [nextViewController onPreEngineRestart];
1291 }
1292 [_platformViewController reset];
1293 _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self];
1294}
1295
1296// This will be called on UI thread, which maybe or may not be platform thread,
1297// depending on the configuration.
1298- (void)onVSync:(uintptr_t)baton {
1299 auto block = ^{
1300 // TODO(knopp): Use vsync waiter for correct view.
1301 // https://github.com/flutter/flutter/issues/142845
1302 FlutterVSyncWaiter* waiter =
1303 [_vsyncWaiters objectForKey:[_vsyncWaiters.keyEnumerator nextObject]];
1304 if (waiter != nil) {
1305 [waiter waitForVSync:baton];
1306 } else {
1307 // Sometimes there is a vsync request right after the last view is removed.
1308 // It still need to be handled, otherwise the engine will stop producing frames
1309 // even if a new view is added later.
1310 self.embedderAPI.OnVsync(_engine, baton, 0, 0);
1311 }
1312 };
1313 if ([NSThread isMainThread]) {
1314 block();
1315 } else {
1316 [FlutterRunLoop.mainRunLoop performBlock:block];
1317 }
1318}
1319
1320/**
1321 * Note: Called from dealloc. Should not use accessors or other methods.
1322 */
1323- (void)shutDownEngine {
1324 if (_engine == nullptr) {
1325 return;
1326 }
1327
1328 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1329 if (result != kSuccess) {
1330 NSLog(@"Could not de-initialize the Flutter engine: error %d", result);
1331 }
1332
1333 result = _embedderAPI.Shutdown(_engine);
1334 if (result != kSuccess) {
1335 NSLog(@"Failed to shut down Flutter engine: error %d", result);
1336 }
1337 _engine = nullptr;
1338}
1339
1340+ (FlutterEngine*)engineForIdentifier:(int64_t)identifier {
1341 NSAssert([[NSThread currentThread] isMainThread], @"Must be called on the main thread.");
1342 return (__bridge FlutterEngine*)reinterpret_cast<void*>(identifier);
1343}
1344
1345- (void)setUpPlatformViewChannel {
1347 [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
1348 binaryMessenger:self.binaryMessenger
1349 codec:[FlutterStandardMethodCodec sharedInstance]];
1350
1351 __weak FlutterEngine* weakSelf = self;
1352 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1353 [[weakSelf platformViewController] handleMethodCall:call result:result];
1354 }];
1355}
1356
1357- (void)setUpAccessibilityChannel {
1359 messageChannelWithName:@"flutter/accessibility"
1360 binaryMessenger:self.binaryMessenger
1362 __weak FlutterEngine* weakSelf = self;
1363 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1364 [weakSelf handleAccessibilityEvent:message];
1365 }];
1366}
1367- (void)setUpNotificationCenterListeners {
1368 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1369 // macOS fires this private message when VoiceOver turns on or off.
1370 [center addObserver:self
1371 selector:@selector(onAccessibilityStatusChanged:)
1372 name:kEnhancedUserInterfaceNotification
1373 object:nil];
1374 [center addObserver:self
1375 selector:@selector(applicationWillTerminate:)
1376 name:NSApplicationWillTerminateNotification
1377 object:nil];
1378 [center addObserver:self
1379 selector:@selector(windowDidChangeScreen:)
1380 name:NSWindowDidChangeScreenNotification
1381 object:nil];
1382 [center addObserver:self
1383 selector:@selector(updateDisplayConfig:)
1384 name:NSApplicationDidChangeScreenParametersNotification
1385 object:nil];
1386}
1387
1388- (void)addInternalPlugins {
1389 __weak FlutterEngine* weakSelf = self;
1390 [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]
1391 delegate:self];
1392 [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
1393
1395 [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel
1396 binaryMessenger:self.binaryMessenger
1399 [FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel
1400 binaryMessenger:self.binaryMessenger
1401 codec:[FlutterJSONMethodCodec sharedInstance]];
1402 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1403 [weakSelf handleMethodCall:call result:result];
1404 }];
1405}
1406
1407- (void)didUpdateMouseCursor:(NSCursor*)cursor {
1408 // Mouse cursor plugin does not specify which view is responsible for changing the cursor,
1409 // so the reasonable assumption here is that cursor change is a result of a mouse movement
1410 // and thus the cursor will be paired with last Flutter view that reveived mouse event.
1411 [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1412}
1413
1414- (void)applicationWillTerminate:(NSNotification*)notification {
1415 [self shutDownEngine];
1416}
1417
1418- (void)windowDidChangeScreen:(NSNotification*)notification {
1419 // Update window metric for all view controllers since the display_id has
1420 // changed.
1421 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1422 FlutterViewController* nextViewController;
1423 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1424 [self updateWindowMetricsForViewController:nextViewController];
1425 }
1426}
1427
1428- (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1429 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1430 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1431 FlutterViewController* nextViewController;
1432 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1433 [nextViewController onAccessibilityStatusChanged:enabled];
1434 }
1435
1436 self.semanticsEnabled = enabled;
1437}
1438- (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
1439 NSString* type = annotatedEvent[@"type"];
1440 if ([type isEqualToString:@"announce"]) {
1441 NSString* message = annotatedEvent[@"data"][@"message"];
1442 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1443 if (message == nil) {
1444 return;
1445 }
1446
1447 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1448 ? NSAccessibilityPriorityHigh
1449 : NSAccessibilityPriorityMedium;
1450
1451 [self announceAccessibilityMessage:message withPriority:priority];
1452 }
1453}
1454
1455- (void)announceAccessibilityMessage:(NSString*)message
1456 withPriority:(NSAccessibilityPriorityLevel)priority {
1457 NSAccessibilityPostNotificationWithUserInfo(
1458 [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1459 NSAccessibilityAnnouncementRequestedNotification,
1460 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1461}
1462- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
1463 if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
1464 [[NSApplication sharedApplication] terminate:self];
1465 result(nil);
1466 } else if ([call.method isEqualToString:@"SystemSound.play"]) {
1467 [self playSystemSound:call.arguments];
1468 result(nil);
1469 } else if ([call.method isEqualToString:@"Clipboard.getData"]) {
1470 result([self getClipboardData:call.arguments]);
1471 } else if ([call.method isEqualToString:@"Clipboard.setData"]) {
1472 [self setClipboardData:call.arguments];
1473 result(nil);
1474 } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) {
1475 result(@{@"value" : @([self clipboardHasStrings])});
1476 } else if ([call.method isEqualToString:@"System.exitApplication"]) {
1477 if ([self terminationHandler] == nil) {
1478 // If the termination handler isn't set, then either we haven't
1479 // initialized it yet, or (more likely) the NSApp delegate isn't a
1480 // FlutterAppDelegate, so it can't cancel requests to exit. So, in that
1481 // case, just terminate when requested.
1482 [NSApp terminate:self];
1483 result(nil);
1484 } else {
1485 [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1486 }
1487 } else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1488 if ([self terminationHandler] != nil) {
1489 [self terminationHandler].acceptingRequests = YES;
1490 }
1491 result(nil);
1492 } else {
1494 }
1495}
1496
1497- (void)playSystemSound:(NSString*)soundType {
1498 if ([soundType isEqualToString:@"SystemSoundType.alert"]) {
1499 NSBeep();
1500 }
1501}
1502
1503- (NSDictionary*)getClipboardData:(NSString*)format {
1504 if ([format isEqualToString:@(kTextPlainFormat)]) {
1505 NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
1506 return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
1507 }
1508 return nil;
1509}
1510
1511- (void)setClipboardData:(NSDictionary*)data {
1512 NSString* text = data[@"text"];
1513 [self.pasteboard clearContents];
1514 if (text && ![text isEqual:[NSNull null]]) {
1515 [self.pasteboard setString:text forType:NSPasteboardTypeString];
1516 }
1517}
1518
1519- (BOOL)clipboardHasStrings {
1520 return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1521}
1522
1523- (std::vector<std::string>)switches {
1525}
1526
1527#pragma mark - FlutterAppLifecycleDelegate
1528
1529- (void)setApplicationState:(flutter::AppLifecycleState)state {
1530 NSString* nextState =
1531 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1532 [self sendOnChannel:kFlutterLifecycleChannel
1533 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1534}
1535
1536/**
1537 * Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
1538 * notification.
1539 */
1540- (void)handleWillBecomeActive:(NSNotification*)notification {
1541 _active = YES;
1542 if (!_visible) {
1543 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1544 } else {
1545 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1546 }
1547}
1548
1549/**
1550 * Called when the |FlutterAppDelegate| gets the applicationWillResignActive
1551 * notification.
1552 */
1553- (void)handleWillResignActive:(NSNotification*)notification {
1554 _active = NO;
1555 if (!_visible) {
1556 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1557 } else {
1558 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1559 }
1560}
1561
1562/**
1563 * Called when the |FlutterAppDelegate| gets the applicationDidUnhide
1564 * notification.
1565 */
1566- (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1567 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1568 if (occlusionState & NSApplicationOcclusionStateVisible) {
1569 _visible = YES;
1570 if (_active) {
1571 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1572 } else {
1573 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1574 }
1575 } else {
1576 _visible = NO;
1577 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1578 }
1579}
1580
1581#pragma mark - FlutterBinaryMessenger
1582
1583- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1584 [self sendOnChannel:channel message:message binaryReply:nil];
1585}
1586
1587- (void)sendOnChannel:(NSString*)channel
1588 message:(NSData* _Nullable)message
1589 binaryReply:(FlutterBinaryReply _Nullable)callback {
1590 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
1591 if (callback) {
1592 struct Captures {
1593 FlutterBinaryReply reply;
1594 };
1595 auto captures = std::make_unique<Captures>();
1596 captures->reply = callback;
1597 auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) {
1598 auto captures = reinterpret_cast<Captures*>(user_data);
1599 NSData* reply_data = nil;
1600 if (data != nullptr && data_size > 0) {
1601 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1602 }
1603 captures->reply(reply_data);
1604 delete captures;
1605 };
1606
1607 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1608 _engine, message_reply, captures.get(), &response_handle);
1609 if (create_result != kSuccess) {
1610 NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1611 return;
1612 }
1613 captures.release();
1614 }
1615
1616 FlutterPlatformMessage platformMessage = {
1618 .channel = [channel UTF8String],
1619 .message = static_cast<const uint8_t*>(message.bytes),
1620 .message_size = message.length,
1621 .response_handle = response_handle,
1622 };
1623
1624 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1625 if (message_result != kSuccess) {
1626 NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1627 message_result);
1628 }
1629
1630 if (response_handle != nullptr) {
1631 FlutterEngineResult release_result =
1632 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1633 if (release_result != kSuccess) {
1634 NSLog(@"Failed to release the response handle (%d).", release_result);
1635 };
1636 }
1637}
1638
1639- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel
1640 binaryMessageHandler:
1641 (nullable FlutterBinaryMessageHandler)handler {
1643 _messengerHandlers[channel] =
1644 [[FlutterEngineHandlerInfo alloc] initWithConnection:@(_currentMessengerConnection)
1645 handler:[handler copy]];
1647}
1648
1649- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1650 // Find the _messengerHandlers that has the required connection, and record its
1651 // channel.
1652 NSString* foundChannel = nil;
1653 for (NSString* key in [_messengerHandlers allKeys]) {
1654 FlutterEngineHandlerInfo* handlerInfo = [_messengerHandlers objectForKey:key];
1655 if ([handlerInfo.connection isEqual:@(connection)]) {
1656 foundChannel = key;
1657 break;
1658 }
1659 }
1660 if (foundChannel) {
1661 [_messengerHandlers removeObjectForKey:foundChannel];
1662 }
1663}
1664
1665#pragma mark - FlutterPluginRegistry
1666
1667- (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1668 id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
1669 if (!registrar) {
1670 FlutterEngineRegistrar* registrarImpl =
1671 [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
1672 self.pluginRegistrars[pluginName] = registrarImpl;
1673 registrar = registrarImpl;
1674 }
1675 return registrar;
1676}
1677
1678- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1679 return self.pluginRegistrars[pluginName].publishedValue;
1680}
1681
1682#pragma mark - FlutterTextureRegistrar
1683
1684- (int64_t)registerTexture:(id<FlutterTexture>)texture {
1685 return [_renderer registerTexture:texture];
1686}
1687
1688- (BOOL)registerTextureWithID:(int64_t)textureId {
1689 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1690}
1691
1692- (void)textureFrameAvailable:(int64_t)textureID {
1693 [_renderer textureFrameAvailable:textureID];
1694}
1695
1696- (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1697 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1698}
1699
1700- (void)unregisterTexture:(int64_t)textureID {
1701 [_renderer unregisterTexture:textureID];
1702}
1703
1704- (BOOL)unregisterTextureWithID:(int64_t)textureID {
1705 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1706}
1707
1708#pragma mark - Task runner integration
1709
1710- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1711 __weak FlutterEngine* weakSelf = self;
1712
1713 const auto engine_time = _embedderAPI.GetCurrentTime();
1714 [FlutterRunLoop.mainRunLoop
1715 performAfterDelay:(targetTime - (double)engine_time) / NSEC_PER_SEC
1716 block:^{
1717 FlutterEngine* self = weakSelf;
1718 if (self != nil && self->_engine != nil) {
1719 auto result = _embedderAPI.RunTask(self->_engine, &task);
1720 if (result != kSuccess) {
1721 NSLog(@"Could not post a task to the Flutter engine.");
1722 }
1723 }
1724 }];
1725}
1726
1727// Getter used by test harness, only exposed through the FlutterEngine(Test) category
1728- (flutter::FlutterCompositor*)macOSCompositor {
1729 return _macOSCompositor.get();
1730}
1731
1732#pragma mark - FlutterKeyboardManagerDelegate
1733
1734/**
1735 * Dispatches the given pointer event data to engine.
1736 */
1737- (void)sendKeyEvent:(const FlutterKeyEvent&)event
1738 callback:(FlutterKeyEventCallback)callback
1739 userData:(void*)userData {
1740 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
1741}
1742
1743@end
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
int64_t FlutterBinaryMessengerConnection
void(^ FlutterResult)(id _Nullable result)
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
FlutterBinaryMessengerConnection _connection
GLenum type
NSInteger clearContents()
int32_t value
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
Definition embedder.cc:3696
#define FLUTTER_API_SYMBOL(symbol)
Definition embedder.h:67
@ kUnfocused
Specifies that a view does not have platform focus.
Definition embedder.h:1191
@ kFocused
Specifies that a view has platform focus.
Definition embedder.h:1194
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition embedder.h:2428
@ kUndefined
Definition embedder.h:1175
void(* FlutterPlatformMessageCallback)(const FlutterPlatformMessage *, void *)
Definition embedder.h:1449
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2324
FlutterThreadPriority
Valid values for priority of Thread.
Definition embedder.h:369
@ kDisplay
Suitable for threads which generate data for the display.
Definition embedder.h:375
@ kRaster
Suitable for thread which raster data.
Definition embedder.h:377
FlutterSemanticsAction
Definition embedder.h:115
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1427
FlutterEngineResult(* FlutterEngineSendPlatformMessageResponseFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessageResponseHandle *handle, const uint8_t *data, size_t data_length)
Definition embedder.h:3619
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
FlutterEngine engine
Definition main.cc:84
FlView * view
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
G_BEGIN_DECLS GBytes * message
const uint8_t uint32_t uint32_t GError ** error
uint32_t * target
G_BEGIN_DECLS FlutterViewId view_id
HWND(* FlutterPlatformViewFactory)(const FlutterPlatformViewCreationParameters *)
FlutterDesktopBinaryReply callback
#define FML_DCHECK(condition)
Definition logging.h:122
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
void(* rootIsolateCreateCallback)(void *_Nullable)
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
NSString * lookupKeyForAsset:(NSString *asset)
FlutterBinaryMessageHandler handler
NSObject< FlutterBinaryMessenger > * binaryMessenger
void registerWithRegistrar:(nonnull id< FlutterPluginRegistrar > registrar)
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
void registerWithRegistrar:delegate:(nonnull id< FlutterPluginRegistrar > registrar,[delegate] nullable id< FlutterMouseCursorPluginDelegate > delegate)
Converts between the time representation used by Flutter Engine and CAMediaTime.
uint64_t CAMediaTimeToEngineTime:(CFTimeInterval time)
void waitForVSync:(uintptr_t baton)
FlutterViewIdentifier viewIdentifier
void onAccessibilityStatusChanged:(BOOL enabled)
FlutterBinaryMessengerRelay * _binaryMessenger
FlutterViewController * viewController
std::u16string text
char ** argv
Definition library.h:9
int64_t FlutterViewIdentifier
FlutterMethodChannel * _platformViewsChannel
_FlutterEngineAOTData * _aotData
std::unique_ptr< flutter::FlutterCompositor > _macOSCompositor
static const int kMainThreadPriority
static void OnPlatformMessage(const FlutterPlatformMessage *message, void *user_data)
FlutterPlatformViewController * _platformViewController
FlutterBasicMessageChannel * _accessibilityChannel
FlutterBasicMessageChannel * _settingsChannel
static FlutterLocale FlutterLocaleFromNSLocale(NSLocale *locale)
BOOL _allowHeadlessExecution
NSMutableDictionary< NSString *, FlutterEngineHandlerInfo * > * _messengerHandlers
FlutterBinaryMessengerConnection _currentMessengerConnection
FlutterMethodChannel * _platformChannel
NSString *const kFlutterLifecycleChannel
static NSString *const kEnhancedUserInterfaceNotification
The private notification for voice over.
NSMapTable< NSNumber *, FlutterVSyncWaiter * > * _vsyncWaiters
FlutterViewIdentifier _nextViewIdentifier
NSString *const kFlutterPlatformChannel
FlutterTextInputPlugin * _textInputPlugin
FlutterDartProject * _project
NSMapTable * _viewControllers
BOOL _multiViewEnabled
FlutterCompositor _compositor
__weak FlutterView * _lastViewWithPointerEvent
FlutterKeyboardManager * _keyboardManager
FlutterWindowController * _windowController
FlutterTerminationCallback _terminator
constexpr char kTextPlainFormat[]
Clipboard plain text format.
__weak FlutterEngine * _flutterEngine
BOOL _visible
BOOL _active
FlutterBinaryMessengerRelay * _binaryMessenger
static NSString *const kEnhancedUserInterfaceKey
NSString *const kFlutterSettingsChannel
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)
constexpr int64_t kFlutterImplicitViewId
Definition constants.h:35
std::vector< std::string > GetSwitchesFromEnvironment()
Definition ref_ptr.h:261
std::vector< FlutterEngineDisplay > * displays
instancetype sharedInstance()
int32_t height
int32_t width
void * user_data
The |FlutterAddViewInfo.user_data|.
Definition embedder.h:1081
FlutterBackingStoreCreateCallback create_backing_store_callback
Definition embedder.h:2213
bool avoid_backing_store_cache
Definition embedder.h:2241
size_t struct_size
This size of this struct. Must be sizeof(FlutterCompositor).
Definition embedder.h:2197
FlutterPresentViewCallback present_view_callback
Definition embedder.h:2250
FlutterBackingStoreCollectCallback collect_backing_store_callback
Definition embedder.h:2218
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
Definition embedder.h:1891
FlutterEngineAOTDataSourceType type
Definition embedder.h:2434
const char * elf_path
Absolute path to an ELF library file.
Definition embedder.h:2437
size_t height
The height of the display, in physical pixels.
Definition embedder.h:2309
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineDisplay).
Definition embedder.h:2291
size_t width
The width of the display, in physical pixels.
Definition embedder.h:2306
FlutterEngineDisplayId display_id
Definition embedder.h:2293
Function-pointer-based versions of the APIs above.
Definition embedder.h:3704
const char * language_code
Definition embedder.h:2259
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:2255
const char * script_code
Definition embedder.h:2269
const char * country_code
Definition embedder.h:2264
const char * variant_code
Definition embedder.h:2274
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1436
FlutterPlatformMessageCallback platform_message_callback
Definition embedder.h:2510
FlutterLogMessageCallback log_message_callback
Definition embedder.h:2702
FlutterViewFocusChangeRequestCallback view_focus_change_request_callback
Definition embedder.h:2760
VsyncCallback vsync_callback
Definition embedder.h:2607
const char * assets_path
Definition embedder.h:2462
OnPreEngineRestartCallback on_pre_engine_restart_callback
Definition embedder.h:2719
FlutterEngineAOTData aot_data
Definition embedder.h:2671
const char *const * dart_entrypoint_argv
Definition embedder.h:2694
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2458
FlutterUpdateSemanticsCallback2 update_semantics_callback2
Definition embedder.h:2749
const char *const * command_line_argv
Definition embedder.h:2504
const char * icu_data_path
Definition embedder.h:2486
bool shutdown_dart_vm_when_done
Definition embedder.h:2640
const char * custom_dart_entrypoint
Definition embedder.h:2616
const FlutterCustomTaskRunners * custom_task_runners
Definition embedder.h:2621
int command_line_argc
The command line argument count used to initialize the project.
Definition embedder.h:2488
VoidCallback root_isolate_create_callback
Definition embedder.h:2545
const FlutterCompositor * compositor
Definition embedder.h:2656
FlutterRemoveViewCallback remove_view_callback
Definition embedder.h:1165
FlutterViewId view_id
Definition embedder.h:1148
A batch of updates to semantics nodes and custom actions.
Definition embedder.h:1796
size_t struct_size
The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
Definition embedder.h:1864
FlutterViewFocusState state
The focus state of the view.
Definition embedder.h:1230
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1047
std::shared_ptr< const fml::Mapping > data
const uintptr_t id
int BOOL