| Flutter macOS Embedder |

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
5 #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h"
6 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h"
7
8 #include <algorithm>
9 #include <iostream>
10 #include <vector>
11
12 #include "flutter/shell/platform/common/app_lifecycle_state.h"
13 #include "flutter/shell/platform/common/engine_switches.h"
14 #include "flutter/shell/platform/embedder/embedder.h"
15
16 #import "flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h"
17 #import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterAppDelegate.h"
18 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterAppDelegate_Internal.h"
19 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterCompositor.h"
20 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h"
21 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDisplayLink.h"
22 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMenuPlugin.h"
23 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterMouseCursorPlugin.h"
24 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterPlatformViewController.h"
25 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterRenderer.h"
26 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTimeConverter.h"
27 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterVSyncWaiter.h"
28 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h"
29 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewEngineProvider.h"
30
31 @class FlutterEngineRegistrar;
32
33 NSString* const kFlutterPlatformChannel = @"flutter/platform";
34 NSString* const kFlutterSettingsChannel = @"flutter/settings";
35 NSString* const kFlutterLifecycleChannel = @"flutter/lifecycle";
36
37 /**
38 * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
39 * the returned struct.
40 */
41 static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
42 FlutterLocale flutterLocale = {};
43 flutterLocale.struct_size = sizeof(FlutterLocale);
44 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
45 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
46 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
47 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
48 return flutterLocale;
49 }
50
51 /// The private notification for voice over.
52 static NSString* const kEnhancedUserInterfaceNotification =
53 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
54 static NSString* const kEnhancedUserInterfaceKey = @"AXEnhancedUserInterface";
55
56 /// Clipboard plain text format.
57 constexpr char kTextPlainFormat[] = "text/plain";
58
59 #pragma mark -
60
61 // Records an active handler of the messenger (FlutterEngine) that listens to
62 // platform messages on a given channel.
63 @interface FlutterEngineHandlerInfo : NSObject
64
65 - (instancetype)initWithConnection:(NSNumber*)connection
66 handler:(FlutterBinaryMessageHandler)handler;
67
68 @property(nonatomic, readonly) FlutterBinaryMessageHandler handler;
69 @property(nonatomic, readonly) NSNumber* connection;
70
71 @end
72
73 @implementation FlutterEngineHandlerInfo
74 - (instancetype)initWithConnection:(NSNumber*)connection
75 handler:(FlutterBinaryMessageHandler)handler {
76 self = [super init];
77 NSAssert(self, @"Super init cannot be nil");
78 _connection = connection;
79 _handler = handler;
80 return self;
81 }
82 @end
83
84 #pragma mark -
85
86 /**
87 * Private interface declaration for FlutterEngine.
88 */
89 @interface FlutterEngine () <FlutterBinaryMessenger>
90
91 /**
92 * A mutable array that holds one bool value that determines if responses to platform messages are
93 * clear to execute. This value should be read or written only inside of a synchronized block and
94 * will return `NO` after the FlutterEngine has been dealloc'd.
95 */
96 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
97
98 /**
99 * All delegates added via plugin calls to addApplicationDelegate.
100 */
101 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
102
103 /**
104 * All registrars returned from registrarForPlugin:
105 */
106 @property(nonatomic, readonly)
107 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
108
109 - (nullable FlutterViewController*)viewControllerForId:(FlutterViewId)viewId;
110
111 /**
112 * An internal method that adds the view controller with the given ID.
113 *
114 * This method assigns the controller with the ID, puts the controller into the
115 * map, and does assertions related to the implicit view ID.
116 */
117 - (void)registerViewController:(FlutterViewController*)controller forId:(FlutterViewId)viewId;
118
119 /**
120 * An internal method that removes the view controller with the given ID.
121 *
122 * This method clears the ID of the controller, removes the controller from the
123 * map. This is an no-op if the view ID is not associated with any view
124 * controllers.
125 */
126 - (void)deregisterViewControllerForId:(FlutterViewId)viewId;
127
128 /**
129 * Shuts down the engine if view requirement is not met, and headless execution
130 * is not allowed.
131 */
132 - (void)shutDownIfNeeded;
133
134 /**
135 * Sends the list of user-preferred locales to the Flutter engine.
136 */
137 - (void)sendUserLocales;
138
139 /**
140 * Handles a platform message from the engine.
141 */
142 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
143
144 /**
145 * Invoked right before the engine is restarted.
146 *
147 * This should reset states to as if the application has just started. It
148 * usually indicates a hot restart (Shift-R in Flutter CLI.)
149 */
150 - (void)engineCallbackOnPreEngineRestart;
151
152 /**
153 * Requests that the task be posted back the to the Flutter engine at the target time. The target
154 * time is in the clock used by the Flutter engine.
155 */
156 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
157
158 /**
159 * Loads the AOT snapshots and instructions from the elf bundle (app_elf_snapshot.so) into _aotData,
160 * if it is present in the assets directory.
161 */
162 - (void)loadAOTData:(NSString*)assetsDir;
163
164 /**
165 * Creates a platform view channel and sets up the method handler.
166 */
167 - (void)setUpPlatformViewChannel;
168
169 /**
170 * Creates an accessibility channel and sets up the message handler.
171 */
172 - (void)setUpAccessibilityChannel;
173
174 /**
175 * Handles messages received from the Flutter engine on the _*Channel channels.
176 */
177 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
178
179 @end
180
181 #pragma mark -
182
183 @implementation FlutterEngineTerminationHandler {
184 __weak FlutterEngine* _engine;
185 FlutterTerminationCallback _terminator;
186 }
187
188 - (instancetype)initWithEngine:(FlutterEngine*)engine
189 terminator:(FlutterTerminationCallback)terminator {
190 self = [super init];
191 _acceptingRequests = NO;
192 _engine = engine;
193 _terminator = terminator ? terminator : ^(id sender) {
194 // Default to actually terminating the application. The terminator exists to
195 // allow tests to override it so that an actual exit doesn't occur.
196 [[NSApplication sharedApplication] terminate:sender];
197 };
198 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
199 if ([appDelegate respondsToSelector:@selector(setTerminationHandler:)]) {
200 FlutterAppDelegate* flutterAppDelegate = reinterpret_cast<FlutterAppDelegate*>(appDelegate);
201 flutterAppDelegate.terminationHandler = self;
202 }
203 return self;
204 }
205
206 // This is called by the method call handler in the engine when the application
207 // requests termination itself.
208 - (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*, id>*)arguments
209 result:(FlutterResult)result {
210 NSString* type = arguments[@"type"];
211 // Ignore the "exitCode" value in the arguments because AppKit doesn't have
212 // any good way to set the process exit code other than calling exit(), and
213 // that bypasses all of the native applicationShouldExit shutdown events,
214 // etc., which we don't want to skip.
215
216 FlutterAppExitType exitType =
217 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
218
219 [self requestApplicationTermination:[NSApplication sharedApplication]
220 exitType:exitType
221 result:result];
222 }
223
224 // This is called by the FlutterAppDelegate whenever any termination request is
225 // received.
226 - (void)requestApplicationTermination:(id)sender
227 exitType:(FlutterAppExitType)type
228 result:(nullable FlutterResult)result {
229 _shouldTerminate = YES;
230 if (![self acceptingRequests]) {
231 // Until the Dart application has signaled that it is ready to handle
232 // termination requests, the app will just terminate when asked.
233 type = kFlutterAppExitTypeRequired;
234 }
235 switch (type) {
236 case kFlutterAppExitTypeCancelable: {
237 FlutterJSONMethodCodec* codec = [FlutterJSONMethodCodec sharedInstance];
238 FlutterMethodCall* methodCall =
239 [FlutterMethodCall methodCallWithMethodName:@"System.requestAppExit" arguments:nil];
240 [_engine sendOnChannel:kFlutterPlatformChannel
241 message:[codec encodeMethodCall:methodCall]
242 binaryReply:^(NSData* _Nullable reply) {
243 NSAssert(_terminator, @"terminator shouldn't be nil");
244 id decoded_reply = [codec decodeEnvelope:reply];
245 if ([decoded_reply isKindOfClass:[FlutterError class]]) {
246 FlutterError* error = (FlutterError*)decoded_reply;
247 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
248 [error details]);
249 _terminator(sender);
250 return;
251 }
252 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
253 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
254 decoded_reply);
255 _terminator(sender);
256 return;
257 }
258 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
259 if ([replyArgs[@"response"] isEqual:@"exit"]) {
260 _terminator(sender);
261 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
262 _shouldTerminate = NO;
263 }
264 if (result != nil) {
265 result(replyArgs);
266 }
267 }];
268 break;
269 }
270 case kFlutterAppExitTypeRequired:
271 NSAssert(_terminator, @"terminator shouldn't be nil");
272 _terminator(sender);
273 break;
274 }
275 }
276
277 @end
278
279 #pragma mark -
280
281 @implementation FlutterPasteboard
282
283 - (NSInteger)clearContents {
284 return [[NSPasteboard generalPasteboard] clearContents];
285 }
286
287 - (NSString*)stringForType:(NSPasteboardType)dataType {
288 return [[NSPasteboard generalPasteboard] stringForType:dataType];
289 }
290
291 - (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
292 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
293 }
294
295 @end
296
297 #pragma mark -
298
299 /**
300 * `FlutterPluginRegistrar` implementation handling a single plugin.
301 */
302 @interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar>
303 - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
304 flutterEngine:(nonnull FlutterEngine*)flutterEngine;
305
306 - (nullable NSView*)viewForId:(FlutterViewId)viewId;
307
308 /**
309 * The value published by this plugin, or NSNull if nothing has been published.
310 *
311 * The unusual NSNull is for the documented behavior of valuePublishedByPlugin:.
312 */
313 @property(nonatomic, readonly, nonnull) NSObject* publishedValue;
314 @end
315
316 @implementation FlutterEngineRegistrar {
317 NSString* _pluginKey;
318 __weak FlutterEngine* _flutterEngine;
319 }
320
321 @dynamic view;
322
323 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
324 self = [super init];
325 if (self) {
326 _pluginKey = [pluginKey copy];
327 _flutterEngine = flutterEngine;
328 _publishedValue = [NSNull null];
329 }
330 return self;
331 }
332
333 #pragma mark - FlutterPluginRegistrar
334
335 - (id<FlutterBinaryMessenger>)messenger {
336 return _flutterEngine.binaryMessenger;
337 }
338
339 - (id<FlutterTextureRegistry>)textures {
340 return _flutterEngine.renderer;
341 }
342
343 - (NSView*)view {
344 return [self viewForId:kFlutterImplicitViewId];
345 }
346
347 - (NSView*)viewForId:(FlutterViewId)viewId {
348 FlutterViewController* controller = [_flutterEngine viewControllerForId:viewId];
349 if (controller == nil) {
350 return nil;
351 }
352 if (!controller.viewLoaded) {
353 [controller loadView];
354 }
355 return controller.flutterView;
356 }
357
358 - (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
359 channel:(nonnull FlutterMethodChannel*)channel {
360 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
361 [delegate handleMethodCall:call result:result];
362 }];
363 }
364
365 - (void)addApplicationDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
366 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
367 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
368 id<FlutterAppLifecycleProvider> lifeCycleProvider =
369 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
370 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
371 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
372 }
373 }
374
375 - (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
376 withId:(nonnull NSString*)factoryId {
377 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
378 }
379
380 - (void)publish:(NSObject*)value {
381 _publishedValue = value;
382 }
383
384 - (NSString*)lookupKeyForAsset:(NSString*)asset {
385 return [FlutterDartProject lookupKeyForAsset:asset];
386 }
387
388 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
389 return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
390 }
391
392 @end
393
394 // Callbacks provided to the engine. See the called methods for documentation.
395 #pragma mark - Static methods provided to engine configuration
396
397 static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngine* engine) {
398 [engine engineCallbackOnPlatformMessage:message];
399 }
400
401 #pragma mark -
402
403 @implementation FlutterEngine {
404 // The embedding-API-level engine object.
405 FLUTTER_API_SYMBOL(FlutterEngine) _engine;
406
407 // The project being run by this engine.
408 FlutterDartProject* _project;
409
410 // A mapping of channel names to the registered information for those channels.
411 NSMutableDictionary<NSString*, FlutterEngineHandlerInfo*>* _messengerHandlers;
412
413 // A self-incremental integer to assign to newly assigned channels as
414 // identification.
415 FlutterBinaryMessengerConnection _currentMessengerConnection;
416
417 // Whether the engine can continue running after the view controller is removed.
418 BOOL _allowHeadlessExecution;
419
420 // Pointer to the Dart AOT snapshot and instruction data.
421 _FlutterEngineAOTData* _aotData;
422
423 // _macOSCompositor is created when the engine is created and its destruction is handled by ARC
424 // when the engine is destroyed.
425 std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
426
427 // The information of all views attached to this engine mapped from IDs.
428 //
429 // It can't use NSDictionary, because the values need to be weak references.
430 NSMapTable* _viewControllers;
431
432 // FlutterCompositor is copied and used in embedder.cc.
433 FlutterCompositor _compositor;
434
435 // Method channel for platform view functions. These functions include creating, disposing and
436 // mutating a platform view.
437 FlutterMethodChannel* _platformViewsChannel;
438
439 // Used to support creation and deletion of platform views and registering platform view
440 // factories. Lifecycle is tied to the engine.
441 FlutterPlatformViewController* _platformViewController;
442
443 // A message channel for sending user settings to the flutter engine.
444 FlutterBasicMessageChannel* _settingsChannel;
445
446 // A message channel for accessibility.
447 FlutterBasicMessageChannel* _accessibilityChannel;
448
449 // A method channel for miscellaneous platform functionality.
450 FlutterMethodChannel* _platformChannel;
451
452 FlutterThreadSynchronizer* _threadSynchronizer;
453
454 // The next available view ID.
455 int _nextViewId;
456
457 // Whether the application is currently the active application.
458 BOOL _active;
459
460 // Whether any portion of the application is currently visible.
461 BOOL _visible;
462
463 // Proxy to allow plugins, channels to hold a weak reference to the binary messenger (self).
464 FlutterBinaryMessengerRelay* _binaryMessenger;
465
466 // Map from ViewId to vsync waiter. Note that this is modified on main thread
467 // but accessed on UI thread, so access must be @synchronized.
468 NSMapTable<NSNumber*, FlutterVSyncWaiter*>* _vsyncWaiters;
469 }
470
471 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
472 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
473 }
474
475 static const int kMainThreadPriority = 47;
476
477 static void SetThreadPriority(FlutterThreadPriority priority) {
478 if (priority == kDisplay || priority == kRaster) {
479 pthread_t thread = pthread_self();
480 sched_param param;
481 int policy;
482 if (!pthread_getschedparam(thread, &policy, &param)) {
483 param.sched_priority = kMainThreadPriority;
484 pthread_setschedparam(thread, policy, &param);
485 }
486 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
487 }
488 }
489
490 - (instancetype)initWithName:(NSString*)labelPrefix
491 project:(FlutterDartProject*)project
492 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
493 self = [super init];
494 NSAssert(self, @"Super init cannot be nil");
495 _pasteboard = [[FlutterPasteboard alloc] init];
496 _active = NO;
497 _visible = NO;
498 _project = project ?: [[FlutterDartProject alloc] init];
499 _messengerHandlers = [[NSMutableDictionary alloc] init];
500 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
501 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
502 _pluginRegistrars = [[NSMutableDictionary alloc] init];
503 _currentMessengerConnection = 1;
504 _allowHeadlessExecution = allowHeadlessExecution;
505 _semanticsEnabled = NO;
506 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
507 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
508 [_isResponseValid addObject:@YES];
509 // kFlutterImplicitViewId is reserved for the implicit view.
510 _nextViewId = kFlutterImplicitViewId + 1;
511
512 _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
513 FlutterEngineGetProcAddresses(&_embedderAPI);
514
515 _viewControllers = [NSMapTable weakToWeakObjectsMapTable];
516 _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self];
517
518 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
519 [notificationCenter addObserver:self
520 selector:@selector(sendUserLocales)
521 name:NSCurrentLocaleDidChangeNotification
522 object:nil];
523
524 _platformViewController = [[FlutterPlatformViewController alloc] init];
525 _threadSynchronizer = [[FlutterThreadSynchronizer alloc] init];
526 [self setUpPlatformViewChannel];
527 [self setUpAccessibilityChannel];
528 [self setUpNotificationCenterListeners];
529 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
530 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
531 _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self
532 terminator:nil];
533 id<FlutterAppLifecycleProvider> lifecycleProvider =
534 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
535 [lifecycleProvider addApplicationLifecycleDelegate:self];
536 } else {
537 _terminationHandler = nil;
538 }
539
540 _vsyncWaiters = [NSMapTable strongToStrongObjectsMapTable];
541
542 return self;
543 }
544
545 - (void)dealloc {
546 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
547 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
548 id<FlutterAppLifecycleProvider> lifecycleProvider =
549 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
550 [lifecycleProvider removeApplicationLifecycleDelegate:self];
551
552 // Unregister any plugins that registered as app delegates, since they are not guaranteed to
553 // live after the engine is destroyed, and their delegation registration is intended to be bound
554 // to the engine and its lifetime.
555 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
556 if (delegate) {
557 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
558 }
559 }
560 }
561 // Clear any published values, just in case a plugin has created a retain cycle with the
562 // registrar.
563 for (NSString* pluginName in _pluginRegistrars) {
564 [_pluginRegistrars[pluginName] publish:[NSNull null]];
565 }
566 @synchronized(_isResponseValid) {
567 [_isResponseValid removeAllObjects];
568 [_isResponseValid addObject:@NO];
569 }
570 [self shutDownEngine];
571 if (_aotData) {
572 _embedderAPI.CollectAOTData(_aotData);
573 }
574 }
575
576 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
577 if (self.running) {
578 return NO;
579 }
580
581 if (!_allowHeadlessExecution && [_viewControllers count] == 0) {
582 NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
583 return NO;
584 }
585
586 [self addInternalPlugins];
587
588 // The first argument of argv is required to be the executable name.
589 std::vector<const char*> argv = {[self.executableName UTF8String]};
590 std::vector<std::string> switches = self.switches;
591
592 // Enable Impeller only if specifically asked for from the project or cmdline arguments.
593 if (_project.enableImpeller ||
594 std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end()) {
595 switches.push_back("--enable-impeller=true");
596 }
597
598 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
599 [](const std::string& arg) -> const char* { return arg.c_str(); });
600
601 std::vector<const char*> dartEntrypointArgs;
602 for (NSString* argument in [_project dartEntrypointArguments]) {
603 dartEntrypointArgs.push_back([argument UTF8String]);
604 }
605
606 FlutterProjectArgs flutterArguments = {};
607 flutterArguments.struct_size = sizeof(FlutterProjectArgs);
608 flutterArguments.assets_path = _project.assetsPath.UTF8String;
609 flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String;
610 flutterArguments.command_line_argc = static_cast<int>(argv.size());
611 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
612 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
613 flutterArguments.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
614 void* user_data) {
615 // TODO(dkwingsmt): This callback only supports single-view, therefore it
616 // only operates on the implicit view. To support multi-view, we need a
617 // way to pass in the ID (probably through FlutterSemanticsUpdate).
618 FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
619 [[engine viewControllerForId:kFlutterImplicitViewId] updateSemantics:update];
620 };
621 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
622 flutterArguments.shutdown_dart_vm_when_done = true;
623 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
624 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
625 flutterArguments.root_isolate_create_callback = _project.rootIsolateCreateCallback;
626 flutterArguments.log_message_callback = [](const char* tag, const char* message,
627 void* user_data) {
628 if (tag && tag[0]) {
629 std::cout << tag << ": ";
630 }
631 std::cout << message << std::endl;
632 };
633
634 static size_t sTaskRunnerIdentifiers = 0;
635 const FlutterTaskRunnerDescription cocoa_task_runner_description = {
636 .struct_size = sizeof(FlutterTaskRunnerDescription),
637 .user_data = (void*)CFBridgingRetain(self),
638 .runs_task_on_current_thread_callback = [](void* user_data) -> bool {
639 return [[NSThread currentThread] isMainThread];
640 },
641 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
642 void* user_data) -> void {
643 [((__bridge FlutterEngine*)(user_data)) postMainThreadTask:task
644 targetTimeInNanoseconds:target_time_nanos];
645 },
646 .identifier = ++sTaskRunnerIdentifiers,
647 };
648 const FlutterCustomTaskRunners custom_task_runners = {
649 .struct_size = sizeof(FlutterCustomTaskRunners),
650 .platform_task_runner = &cocoa_task_runner_description,
651 .thread_priority_setter = SetThreadPriority};
652 flutterArguments.custom_task_runners = &custom_task_runners;
653
654 [self loadAOTData:_project.assetsPath];
655 if (_aotData) {
656 flutterArguments.aot_data = _aotData;
657 }
658
659 flutterArguments.compositor = [self createFlutterCompositor];
660
661 flutterArguments.on_pre_engine_restart_callback = [](void* user_data) {
662 FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
663 [engine engineCallbackOnPreEngineRestart];
664 };
665
666 flutterArguments.vsync_callback = [](void* user_data, intptr_t baton) {
667 FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
668 [engine onVSync:baton];
669 };
670
671 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
672 FlutterEngineResult result = _embedderAPI.Initialize(
673 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
674 if (result != kSuccess) {
675 NSLog(@"Failed to initialize Flutter engine: error %d", result);
676 return NO;
677 }
678
679 result = _embedderAPI.RunInitialized(_engine);
680 if (result != kSuccess) {
681 NSLog(@"Failed to run an initialized engine: error %d", result);
682 return NO;
683 }
684
685 [self sendUserLocales];
686
687 // Update window metric for all view controllers.
688 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
689 FlutterViewController* nextViewController;
690 while ((nextViewController = [viewControllerEnumerator nextObject])) {
691 [self updateWindowMetricsForViewController:nextViewController];
692 }
693
694 [self updateDisplayConfig];
695 // Send the initial user settings such as brightness and text scale factor
696 // to the engine.
697 [self sendInitialSettings];
698 return YES;
699 }
700
701 - (void)loadAOTData:(NSString*)assetsDir {
702 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
703 return;
704 }
705
706 BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath.
707 NSFileManager* fileManager = [NSFileManager defaultManager];
708
709 // This is the location where the test fixture places the snapshot file.
710 // For applications built by Flutter tool, this is in "App.framework".
711 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
712
713 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
714 return;
715 }
716
717 FlutterEngineAOTDataSource source = {};
718 source.type = kFlutterEngineAOTDataSourceTypeElfPath;
719 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
720
721 auto result = _embedderAPI.CreateAOTData(&source, &_aotData);
722 if (result != kSuccess) {
723 NSLog(@"Failed to load AOT data from: %@", elfPath);
724 }
725 }
726
727 - (void)registerViewController:(FlutterViewController*)controller forId:(FlutterViewId)viewId {
728 NSAssert(controller != nil, @"The controller must not be nil.");
729 NSAssert(![controller attached],
730 @"The incoming view controller is already attached to an engine.");
731 NSAssert([_viewControllers objectForKey:@(viewId)] == nil, @"The requested view ID is occupied.");
732 [controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer];
733 NSAssert(controller.viewId == viewId, @"Failed to assign view ID.");
734 [_viewControllers setObject:controller forKey:@(viewId)];
735
736 if (controller.viewLoaded) {
737 [self viewControllerViewDidLoad:controller];
738 }
739 }
740
741 - (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
742 __weak FlutterEngine* weakSelf = self;
743 FlutterTimeConverter* timeConverter = [[FlutterTimeConverter alloc] initWithEngine:self];
744 FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
745 initWithDisplayLink:[FlutterDisplayLink displayLinkWithView:viewController.view]
746 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
747 uintptr_t baton) {
748 uint64_t timeNanos = [timeConverter CAMediaTimeToEngineTime:timestamp];
749 uint64_t targetTimeNanos =
750 [timeConverter CAMediaTimeToEngineTime:targetTimestamp];
751 FlutterEngine* engine = weakSelf;
752 if (engine) {
753 // It is a bit unfortunate that embedder requires OnVSync call on
754 // platform thread just to immediately redispatch it to UI thread.
755 // We are already on UI thread right now, but have to do the
756 // extra hop to main thread.
757 [engine->_threadSynchronizer performOnPlatformThread:^{
758 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
759 }];
760 }
761 }];
762 FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewId)] == nil);
763 @synchronized(_vsyncWaiters) {
764 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewId)];
765 }
766 }
767
768 - (void)deregisterViewControllerForId:(FlutterViewId)viewId {
769 FlutterViewController* oldController = [self viewControllerForId:viewId];
770 if (oldController != nil) {
771 [oldController detachFromEngine];
772 [_viewControllers removeObjectForKey:@(viewId)];
773 }
774 @synchronized(_vsyncWaiters) {
775 [_vsyncWaiters removeObjectForKey:@(viewId)];
776 }
777 }
778
779 - (void)shutDownIfNeeded {
780 if ([_viewControllers count] == 0 && !_allowHeadlessExecution) {
781 [self shutDownEngine];
782 }
783 }
784
785 - (FlutterViewController*)viewControllerForId:(FlutterViewId)viewId {
786 FlutterViewController* controller = [_viewControllers objectForKey:@(viewId)];
787 NSAssert(controller == nil || controller.viewId == viewId,
788 @"The stored controller has unexpected view ID.");
789 return controller;
790 }
791
792 - (void)setViewController:(FlutterViewController*)controller {
793 FlutterViewController* currentController =
794 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
795 if (currentController == controller) {
796 // From nil to nil, or from non-nil to the same controller.
797 return;
798 }
799 if (currentController == nil && controller != nil) {
800 // From nil to non-nil.
801 NSAssert(controller.engine == nil,
802 @"Failed to set view controller to the engine: "
803 @"The given FlutterViewController is already attached to an engine %@. "
804 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
805 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
806 controller.engine);
807 [self registerViewController:controller forId:kFlutterImplicitViewId];
808 } else if (currentController != nil && controller == nil) {
809 NSAssert(currentController.viewId == kFlutterImplicitViewId,
810 @"The default controller has an unexpected ID %llu", currentController.viewId);
811 // From non-nil to nil.
812 [self deregisterViewControllerForId:kFlutterImplicitViewId];
813 [self shutDownIfNeeded];
814 } else {
815 // From non-nil to a different non-nil view controller.
816 NSAssert(NO,
817 @"Failed to set view controller to the engine: "
818 @"The engine already has an implicit view controller %@. "
819 @"If you wanted to make the implicit view render in a different window, "
820 @"you should attach the current view controller to the window instead.",
821 [_viewControllers objectForKey:@(kFlutterImplicitViewId)]);
822 }
823 }
824
825 - (FlutterViewController*)viewController {
826 return [self viewControllerForId:kFlutterImplicitViewId];
827 }
828
829 - (FlutterCompositor*)createFlutterCompositor {
830 _macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
831 [[FlutterViewEngineProvider alloc] initWithEngine:self],
832 [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController);
833
834 _compositor = {};
835 _compositor.struct_size = sizeof(FlutterCompositor);
836 _compositor.user_data = _macOSCompositor.get();
837
838 _compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, //
839 FlutterBackingStore* backing_store_out, //
840 void* user_data //
841 ) {
842 return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
843 config, backing_store_out);
844 };
845
846 _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, //
847 void* user_data //
848 ) { return true; };
849
850 _compositor.present_layers_callback = [](const FlutterLayer** layers, //
851 size_t layers_count, //
852 void* user_data //
853 ) {
854 // TODO(dkwingsmt): This callback only supports single-view, therefore it
855 // only operates on the implicit view. To support multi-view, we need a new
856 // callback that also receives a view ID.
857 return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->Present(kFlutterImplicitViewId,
858 layers, layers_count);
859 };
860
861 _compositor.avoid_backing_store_cache = true;
862
863 return &_compositor;
864 }
865
866 - (id<FlutterBinaryMessenger>)binaryMessenger {
867 return _binaryMessenger;
868 }
869
870 #pragma mark - Framework-internal methods
871
872 - (void)addViewController:(FlutterViewController*)controller {
873 [self registerViewController:controller forId:kFlutterImplicitViewId];
874 }
875
876 - (void)removeViewController:(nonnull FlutterViewController*)viewController {
877 NSAssert([viewController attached] && viewController.engine == self,
878 @"The given view controller is not associated with this engine.");
879 [self deregisterViewControllerForId:viewController.viewId];
880 [self shutDownIfNeeded];
881 }
882
883 - (BOOL)running {
884 return _engine != nullptr;
885 }
886
887 - (void)updateDisplayConfig:(NSNotification*)notification {
888 [self updateDisplayConfig];
889 }
890
891 - (NSArray<NSScreen*>*)screens {
892 return [NSScreen screens];
893 }
894
895 - (void)updateDisplayConfig {
896 if (!_engine) {
897 return;
898 }
899
900 std::vector<FlutterEngineDisplay> displays;
901 for (NSScreen* screen : [self screens]) {
902 CGDirectDisplayID displayID =
903 static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
904
905 double devicePixelRatio = screen.backingScaleFactor;
906 FlutterEngineDisplay display;
907 display.struct_size = sizeof(display);
908 display.display_id = displayID;
909 display.single_display = false;
910 display.width = static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
911 display.height = static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
912 display.device_pixel_ratio = devicePixelRatio;
913
914 CVDisplayLinkRef displayLinkRef = nil;
915 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
916
917 if (error == 0) {
918 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
919 if (!(nominal.flags & kCVTimeIsIndefinite)) {
920 double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
921 display.refresh_rate = round(refreshRate);
922 }
923 CVDisplayLinkRelease(displayLinkRef);
924 } else {
925 display.refresh_rate = 0;
926 }
927
928 displays.push_back(display);
929 }
930 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
931 displays.data(), displays.size());
932 }
933
934 - (void)onSettingsChanged:(NSNotification*)notification {
935 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
936 NSString* brightness =
937 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
938 [_settingsChannel sendMessage:@{
939 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
940 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
941 @"textScaleFactor" : @1.0,
942 @"alwaysUse24HourFormat" : @false
943 }];
944 }
945
946 - (void)sendInitialSettings {
947 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
948 [[NSDistributedNotificationCenter defaultCenter]
949 addObserver:self
950 selector:@selector(onSettingsChanged:)
951 name:@"AppleInterfaceThemeChangedNotification"
952 object:nil];
953 [self onSettingsChanged:nil];
954 }
955
956 - (FlutterEngineProcTable&)embedderAPI {
957 return _embedderAPI;
958 }
959
960 - (nonnull NSString*)executableName {
961 return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter";
962 }
963
964 - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
965 if (!_engine || !viewController || !viewController.viewLoaded) {
966 return;
967 }
968 NSAssert([self viewControllerForId:viewController.viewId] == viewController,
969 @"The provided view controller is not attached to this engine.");
970 NSView* view = viewController.flutterView;
971 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
972 CGSize scaledSize = scaledBounds.size;
973 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
974 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
975 const FlutterWindowMetricsEvent windowMetricsEvent = {
976 .struct_size = sizeof(windowMetricsEvent),
977 .width = static_cast<size_t>(scaledSize.width),
978 .height = static_cast<size_t>(scaledSize.height),
979 .pixel_ratio = pixelRatio,
980 .left = static_cast<size_t>(scaledBounds.origin.x),
981 .top = static_cast<size_t>(scaledBounds.origin.y),
982 .display_id = static_cast<uint64_t>(displayId),
983 .view_id = viewController.viewId,
984 };
985 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
986 }
987
988 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
989 _embedderAPI.SendPointerEvent(_engine, &event, 1);
990 }
991
992 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
993 callback:(FlutterKeyEventCallback)callback
994 userData:(void*)userData {
995 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
996 }
997
998 - (void)setSemanticsEnabled:(BOOL)enabled {
999 if (_semanticsEnabled == enabled) {
1000 return;
1001 }
1002 _semanticsEnabled = enabled;
1003
1004 // Update all view controllers' bridges.
1005 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1006 FlutterViewController* nextViewController;
1007 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1008 [nextViewController notifySemanticsEnabledChanged];
1009 }
1010
1011 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1012 }
1013
1014 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1015 toTarget:(uint16_t)target
1016 withData:(fml::MallocMapping)data {
1017 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1018 }
1019
1020 - (FlutterPlatformViewController*)platformViewController {
1021 return _platformViewController;
1022 }
1023
1024 #pragma mark - Private methods
1025
1026 - (void)sendUserLocales {
1027 if (!self.running) {
1028 return;
1029 }
1030
1031 // Create a list of FlutterLocales corresponding to the preferred languages.
1032 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1033 std::vector<FlutterLocale> flutterLocales;
1034 flutterLocales.reserve(locales.count);
1035 for (NSString* localeID in [NSLocale preferredLanguages]) {
1036 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1037 [locales addObject:locale];
1038 flutterLocales.push_back(FlutterLocaleFromNSLocale(locale));
1039 }
1040 // Convert to a list of pointers, and send to the engine.
1041 std::vector<const FlutterLocale*> flutterLocaleList;
1042 flutterLocaleList.reserve(flutterLocales.size());
1043 std::transform(flutterLocales.begin(), flutterLocales.end(),
1044 std::back_inserter(flutterLocaleList),
1045 [](const auto& arg) -> const auto* { return &arg; });
1046 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1047 }
1048
1049 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1050 NSData* messageData = nil;
1051 if (message->message_size > 0) {
1052 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1053 length:message->message_size
1054 freeWhenDone:NO];
1055 }
1056 NSString* channel = @(message->channel);
1057 __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1058 __block FlutterEngine* weakSelf = self;
1059 NSMutableArray* isResponseValid = self.isResponseValid;
1060 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1061 _embedderAPI.SendPlatformMessageResponse;
1062 FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
1063 @synchronized(isResponseValid) {
1064 if (![isResponseValid[0] boolValue]) {
1065 // Ignore, engine was killed.
1066 return;
1067 }
1068 if (responseHandle) {
1069 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1070 static_cast<const uint8_t*>(response.bytes), response.length);
1071 responseHandle = NULL;
1072 } else {
1073 NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
1074 "on channel '%@'.",
1075 channel);
1076 }
1077 }
1078 };
1079
1080 FlutterEngineHandlerInfo* handlerInfo = _messengerHandlers[channel];
1081 if (handlerInfo) {
1082 handlerInfo.handler(messageData, binaryResponseHandler);
1083 } else {
1084 binaryResponseHandler(nil);
1085 }
1086 }
1087
1088 - (void)engineCallbackOnPreEngineRestart {
1089 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1090 FlutterViewController* nextViewController;
1091 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1092 [nextViewController onPreEngineRestart];
1093 }
1094 }
1095
1096 - (void)onVSync:(uintptr_t)baton {
1097 @synchronized(_vsyncWaiters) {
1098 // TODO(knopp): Use vsync waiter for correct view.
1099 // https://github.com/flutter/flutter/issues/142845
1100 FlutterVSyncWaiter* waiter = [_vsyncWaiters objectForKey:@(kFlutterImplicitViewId)];
1101 [waiter waitForVSync:baton];
1102 }
1103 }
1104
1105 /**
1106 * Note: Called from dealloc. Should not use accessors or other methods.
1107 */
1108 - (void)shutDownEngine {
1109 if (_engine == nullptr) {
1110 return;
1111 }
1112
1113 [_threadSynchronizer shutdown];
1114 _threadSynchronizer = nil;
1115
1116 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1117 if (result != kSuccess) {
1118 NSLog(@"Could not de-initialize the Flutter engine: error %d", result);
1119 }
1120
1121 // Balancing release for the retain in the task runner dispatch table.
1122 CFRelease((CFTypeRef)self);
1123
1124 result = _embedderAPI.Shutdown(_engine);
1125 if (result != kSuccess) {
1126 NSLog(@"Failed to shut down Flutter engine: error %d", result);
1127 }
1128 _engine = nullptr;
1129 }
1130
1131 - (void)setUpPlatformViewChannel {
1132 _platformViewsChannel =
1133 [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
1134 binaryMessenger:self.binaryMessenger
1135 codec:[FlutterStandardMethodCodec sharedInstance]];
1136
1137 __weak FlutterEngine* weakSelf = self;
1138 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1139 [[weakSelf platformViewController] handleMethodCall:call result:result];
1140 }];
1141 }
1142
1143 - (void)setUpAccessibilityChannel {
1144 _accessibilityChannel = [FlutterBasicMessageChannel
1145 messageChannelWithName:@"flutter/accessibility"
1146 binaryMessenger:self.binaryMessenger
1147 codec:[FlutterStandardMessageCodec sharedInstance]];
1148 __weak FlutterEngine* weakSelf = self;
1149 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1150 [weakSelf handleAccessibilityEvent:message];
1151 }];
1152 }
1153 - (void)setUpNotificationCenterListeners {
1154 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1155 // macOS fires this private message when VoiceOver turns on or off.
1156 [center addObserver:self
1157 selector:@selector(onAccessibilityStatusChanged:)
1158 name:kEnhancedUserInterfaceNotification
1159 object:nil];
1160 [center addObserver:self
1161 selector:@selector(applicationWillTerminate:)
1162 name:NSApplicationWillTerminateNotification
1163 object:nil];
1164 [center addObserver:self
1165 selector:@selector(windowDidChangeScreen:)
1166 name:NSWindowDidChangeScreenNotification
1167 object:nil];
1168 [center addObserver:self
1169 selector:@selector(updateDisplayConfig:)
1170 name:NSApplicationDidChangeScreenParametersNotification
1171 object:nil];
1172 }
1173
1174 - (void)addInternalPlugins {
1175 __weak FlutterEngine* weakSelf = self;
1176 [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]];
1177 [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
1178 _settingsChannel =
1179 [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel
1180 binaryMessenger:self.binaryMessenger
1181 codec:[FlutterJSONMessageCodec sharedInstance]];
1182 _platformChannel =
1183 [FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel
1184 binaryMessenger:self.binaryMessenger
1185 codec:[FlutterJSONMethodCodec sharedInstance]];
1186 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1187 [weakSelf handleMethodCall:call result:result];
1188 }];
1189 }
1190
1191 - (void)applicationWillTerminate:(NSNotification*)notification {
1192 [self shutDownEngine];
1193 }
1194
1195 - (void)windowDidChangeScreen:(NSNotification*)notification {
1196 // Update window metric for all view controllers since the display_id has
1197 // changed.
1198 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1199 FlutterViewController* nextViewController;
1200 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1201 [self updateWindowMetricsForViewController:nextViewController];
1202 }
1203 }
1204
1205 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1206 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1207 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1208 FlutterViewController* nextViewController;
1209 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1210 [nextViewController onAccessibilityStatusChanged:enabled];
1211 }
1212
1213 self.semanticsEnabled = enabled;
1214 }
1215 - (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
1216 NSString* type = annotatedEvent[@"type"];
1217 if ([type isEqualToString:@"announce"]) {
1218 NSString* message = annotatedEvent[@"data"][@"message"];
1219 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1220 if (message == nil) {
1221 return;
1222 }
1223
1224 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1225 ? NSAccessibilityPriorityHigh
1226 : NSAccessibilityPriorityMedium;
1227
1228 [self announceAccessibilityMessage:message withPriority:priority];
1229 }
1230 }
1231
1232 - (void)announceAccessibilityMessage:(NSString*)message
1233 withPriority:(NSAccessibilityPriorityLevel)priority {
1234 NSAccessibilityPostNotificationWithUserInfo(
1235 [self viewControllerForId:kFlutterImplicitViewId].flutterView,
1236 NSAccessibilityAnnouncementRequestedNotification,
1237 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1238 }
1239 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
1240 if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
1241 [[NSApplication sharedApplication] terminate:self];
1242 result(nil);
1243 } else if ([call.method isEqualToString:@"SystemSound.play"]) {
1244 [self playSystemSound:call.arguments];
1245 result(nil);
1246 } else if ([call.method isEqualToString:@"Clipboard.getData"]) {
1247 result([self getClipboardData:call.arguments]);
1248 } else if ([call.method isEqualToString:@"Clipboard.setData"]) {
1249 [self setClipboardData:call.arguments];
1250 result(nil);
1251 } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) {
1252 result(@{@"value" : @([self clipboardHasStrings])});
1253 } else if ([call.method isEqualToString:@"System.exitApplication"]) {
1254 if ([self terminationHandler] == nil) {
1255 // If the termination handler isn't set, then either we haven't
1256 // initialized it yet, or (more likely) the NSApp delegate isn't a
1257 // FlutterAppDelegate, so it can't cancel requests to exit. So, in that
1258 // case, just terminate when requested.
1259 [NSApp terminate:self];
1260 result(nil);
1261 } else {
1262 [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1263 }
1264 } else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1265 if ([self terminationHandler] != nil) {
1266 [self terminationHandler].acceptingRequests = YES;
1267 }
1268 result(nil);
1269 } else {
1270 result(FlutterMethodNotImplemented);
1271 }
1272 }
1273
1274 - (void)playSystemSound:(NSString*)soundType {
1275 if ([soundType isEqualToString:@"SystemSoundType.alert"]) {
1276 NSBeep();
1277 }
1278 }
1279
1280 - (NSDictionary*)getClipboardData:(NSString*)format {
1281 if ([format isEqualToString:@(kTextPlainFormat)]) {
1282 NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
1283 return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
1284 }
1285 return nil;
1286 }
1287
1288 - (void)setClipboardData:(NSDictionary*)data {
1289 NSString* text = data[@"text"];
1290 [self.pasteboard clearContents];
1291 if (text && ![text isEqual:[NSNull null]]) {
1292 [self.pasteboard setString:text forType:NSPasteboardTypeString];
1293 }
1294 }
1295
1296 - (BOOL)clipboardHasStrings {
1297 return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1298 }
1299
1300 - (std::vector<std::string>)switches {
1301 return flutter::GetSwitchesFromEnvironment();
1302 }
1303
1304 - (FlutterThreadSynchronizer*)testThreadSynchronizer {
1305 return _threadSynchronizer;
1306 }
1307
1308 #pragma mark - FlutterAppLifecycleDelegate
1309
1310 - (void)setApplicationState:(flutter::AppLifecycleState)state {
1311 NSString* nextState =
1312 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1313 [self sendOnChannel:kFlutterLifecycleChannel
1314 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1315 }
1316
1317 /**
1318 * Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
1319 * notification.
1320 */
1321 - (void)handleWillBecomeActive:(NSNotification*)notification {
1322 _active = YES;
1323 if (!_visible) {
1324 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1325 } else {
1326 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1327 }
1328 }
1329
1330 /**
1331 * Called when the |FlutterAppDelegate| gets the applicationWillResignActive
1332 * notification.
1333 */
1334 - (void)handleWillResignActive:(NSNotification*)notification {
1335 _active = NO;
1336 if (!_visible) {
1337 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1338 } else {
1339 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1340 }
1341 }
1342
1343 /**
1344 * Called when the |FlutterAppDelegate| gets the applicationDidUnhide
1345 * notification.
1346 */
1347 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1348 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1349 if (occlusionState & NSApplicationOcclusionStateVisible) {
1350 _visible = YES;
1351 if (_active) {
1352 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1353 } else {
1354 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1355 }
1356 } else {
1357 _visible = NO;
1358 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1359 }
1360 }
1361
1362 #pragma mark - FlutterBinaryMessenger
1363
1364 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1365 [self sendOnChannel:channel message:message binaryReply:nil];
1366 }
1367
1368 - (void)sendOnChannel:(NSString*)channel
1369 message:(NSData* _Nullable)message
1370 binaryReply:(FlutterBinaryReply _Nullable)callback {
1371 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
1372 if (callback) {
1373 struct Captures {
1374 FlutterBinaryReply reply;
1375 };
1376 auto captures = std::make_unique<Captures>();
1377 captures->reply = callback;
1378 auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) {
1379 auto captures = reinterpret_cast<Captures*>(user_data);
1380 NSData* reply_data = nil;
1381 if (data != nullptr && data_size > 0) {
1382 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1383 }
1384 captures->reply(reply_data);
1385 delete captures;
1386 };
1387
1388 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1389 _engine, message_reply, captures.get(), &response_handle);
1390 if (create_result != kSuccess) {
1391 NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1392 return;
1393 }
1394 captures.release();
1395 }
1396
1397 FlutterPlatformMessage platformMessage = {
1398 .struct_size = sizeof(FlutterPlatformMessage),
1399 .channel = [channel UTF8String],
1400 .message = static_cast<const uint8_t*>(message.bytes),
1401 .message_size = message.length,
1402 .response_handle = response_handle,
1403 };
1404
1405 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1406 if (message_result != kSuccess) {
1407 NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1408 message_result);
1409 }
1410
1411 if (response_handle != nullptr) {
1412 FlutterEngineResult release_result =
1413 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1414 if (release_result != kSuccess) {
1415 NSLog(@"Failed to release the response handle (%d).", release_result);
1416 };
1417 }
1418 }
1419
1420 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel
1421 binaryMessageHandler:
1422 (nullable FlutterBinaryMessageHandler)handler {
1423 _currentMessengerConnection += 1;
1424 _messengerHandlers[channel] =
1425 [[FlutterEngineHandlerInfo alloc] initWithConnection:@(_currentMessengerConnection)
1426 handler:[handler copy]];
1427 return _currentMessengerConnection;
1428 }
1429
1430 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1431 // Find the _messengerHandlers that has the required connection, and record its
1432 // channel.
1433 NSString* foundChannel = nil;
1434 for (NSString* key in [_messengerHandlers allKeys]) {
1435 FlutterEngineHandlerInfo* handlerInfo = [_messengerHandlers objectForKey:key];
1436 if ([handlerInfo.connection isEqual:@(connection)]) {
1437 foundChannel = key;
1438 break;
1439 }
1440 }
1441 if (foundChannel) {
1442 [_messengerHandlers removeObjectForKey:foundChannel];
1443 }
1444 }
1445
1446 #pragma mark - FlutterPluginRegistry
1447
1448 - (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1449 id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
1450 if (!registrar) {
1451 FlutterEngineRegistrar* registrarImpl =
1452 [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
1453 self.pluginRegistrars[pluginName] = registrarImpl;
1454 registrar = registrarImpl;
1455 }
1456 return registrar;
1457 }
1458
1459 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1460 return self.pluginRegistrars[pluginName].publishedValue;
1461 }
1462
1463 #pragma mark - FlutterTextureRegistrar
1464
1465 - (int64_t)registerTexture:(id<FlutterTexture>)texture {
1466 return [_renderer registerTexture:texture];
1467 }
1468
1469 - (BOOL)registerTextureWithID:(int64_t)textureId {
1470 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1471 }
1472
1473 - (void)textureFrameAvailable:(int64_t)textureID {
1474 [_renderer textureFrameAvailable:textureID];
1475 }
1476
1477 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1478 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1479 }
1480
1481 - (void)unregisterTexture:(int64_t)textureID {
1482 [_renderer unregisterTexture:textureID];
1483 }
1484
1485 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1486 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1487 }
1488
1489 #pragma mark - Task runner integration
1490
1491 - (void)runTaskOnEmbedder:(FlutterTask)task {
1492 if (_engine) {
1493 auto result = _embedderAPI.RunTask(_engine, &task);
1494 if (result != kSuccess) {
1495 NSLog(@"Could not post a task to the Flutter engine.");
1496 }
1497 }
1498 }
1499
1500 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1501 __weak FlutterEngine* weakSelf = self;
1502 auto worker = ^{
1503 [weakSelf runTaskOnEmbedder:task];
1504 };
1505
1506 const auto engine_time = _embedderAPI.GetCurrentTime();
1507 if (targetTime <= engine_time) {
1508 dispatch_async(dispatch_get_main_queue(), worker);
1509
1510 } else {
1511 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, targetTime - engine_time),
1512 dispatch_get_main_queue(), worker);
1513 }
1514 }
1515
1516 // Getter used by test harness, only exposed through the FlutterEngine(Test) category
1517 - (flutter::FlutterCompositor*)macOSCompositor {
1518 return _macOSCompositor.get();
1519 }
1520
1521 @end
kEnhancedUserInterfaceKey
static NSString *const kEnhancedUserInterfaceKey
Definition: FlutterEngine.mm:54
+[FlutterBasicMessageChannel messageChannelWithName:binaryMessenger:codec:]
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
Definition: FlutterChannels.mm:82
_terminator
FlutterTerminationCallback _terminator
Definition: FlutterEngine.mm:183
FlutterPasteboard
Definition: FlutterEngine.mm:281
FlutterAppDelegate_Internal.h
_platformChannel
FlutterMethodChannel * _platformChannel
Definition: FlutterEngine.mm:450
FlutterEngine
Definition: FlutterEngine.h:30
FlutterMouseCursorPlugin
Definition: FlutterMouseCursorPlugin.h:19
_vsyncWaiters
NSMapTable< NSNumber *, FlutterVSyncWaiter * > * _vsyncWaiters
Definition: FlutterEngine.mm:468
FlutterPlugin-p
Definition: FlutterPluginMacOS.h:29
_platformViewController
FlutterPlatformViewController * _platformViewController
Definition: FlutterEngine.mm:441
kTextPlainFormat
constexpr char kTextPlainFormat[]
Clipboard plain text format.
Definition: FlutterEngine.mm:57
kFlutterPlatformChannel
NSString *const kFlutterPlatformChannel
Definition: FlutterEngine.mm:33
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterBasicMessageChannel
Definition: FlutterChannels.h:37
FlutterTimeConverter.h
FlutterViewController
Definition: FlutterViewController.h:65
FlutterMethodChannel
Definition: FlutterChannels.h:220
FlutterEngine.h
FlutterMethodNotImplemented
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
-[FlutterViewController onAccessibilityStatusChanged:]
void onAccessibilityStatusChanged:(BOOL enabled)
FlutterPlatformViewController
Definition: FlutterPlatformViewController.h:17
FlutterTextureRegistry-p
Definition: FlutterTexture.h:38
FlutterVSyncWaiter.h
FlutterPlatformViewController.h
FlutterEngineHandlerInfo::handler
FlutterBinaryMessageHandler handler
Definition: FlutterEngine.mm:68
user_data
void * user_data
Definition: texture_registrar_unittests.cc:27
FlutterEngine_Internal.h
+[FlutterDartProject lookupKeyForAsset:]
NSString * lookupKeyForAsset:(NSString *asset)
Definition: FlutterDartProject.mm:116
FlutterError
Definition: FlutterCodecs.h:246
FlutterViewEngineProvider.h
flutter::FlutterCompositor
Definition: FlutterCompositor.h:30
_nextViewId
int _nextViewId
Definition: FlutterEngine.mm:455
_currentMessengerConnection
FlutterBinaryMessengerConnection _currentMessengerConnection
Definition: FlutterEngine.mm:415
FlutterMethodCall::method
NSString * method
Definition: FlutterCodecs.h:233
kMainThreadPriority
static const int kMainThreadPriority
Definition: FlutterEngine.mm:475
_settingsChannel
FlutterBasicMessageChannel * _settingsChannel
Definition: FlutterEngine.mm:444
FlutterRenderer.h
_project
FlutterDartProject * _project
Definition: FlutterEngine.mm:403
FlutterVSyncWaiter
Definition: FlutterVSyncWaiter.h:8
FlutterViewController::engine
FlutterEngine * engine
Definition: FlutterViewController.h:70
FlutterEngineRegistrar::publishedValue
NSObject * publishedValue
Definition: FlutterEngine.mm:313
FlutterDisplayLink
Definition: FlutterDisplayLink.h:15
FlutterPluginRegistrar-p
Definition: FlutterPluginRegistrarMacOS.h:28
FlutterAppLifecycleProvider-p
Definition: FlutterAppDelegate.h:21
_viewControllers
NSMapTable * _viewControllers
Definition: FlutterEngine.mm:430
FlutterEngine::binaryMessenger
id< FlutterBinaryMessenger > binaryMessenger
Definition: FlutterEngine.h:91
app_lifecycle_state.h
FlutterViewEngineProvider
Definition: FlutterViewEngineProvider.h:18
kFlutterImplicitViewId
constexpr FlutterViewId kFlutterImplicitViewId
Definition: FlutterView.h:26
_macOSCompositor
std::unique_ptr< flutter::FlutterCompositor > _macOSCompositor
Definition: FlutterEngine.mm:425
FlutterAppLifecycleDelegate-p
Definition: FlutterAppLifecycleDelegate.h:21
FlutterRenderer
Definition: FlutterRenderer.h:18
+[FlutterPlugin-p registerWithRegistrar:]
void registerWithRegistrar:(id< FlutterPluginRegistrar > registrar)
FlutterBinaryMessageHandler
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
Definition: FlutterBinaryMessenger.h:30
-[FlutterVSyncWaiter waitForVSync:]
void waitForVSync:(uintptr_t baton)
Definition: FlutterVSyncWaiter.mm:108
kEnhancedUserInterfaceNotification
static NSString *const kEnhancedUserInterfaceNotification
The private notification for voice over.
Definition: FlutterEngine.mm:52
FlutterMouseCursorPlugin.h
-[FlutterMethodChannel setMethodCallHandler:]
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
FlutterStandardMessageCodec
Definition: FlutterCodecs.h:209
FlutterBinaryMessengerRelay.h
-[FlutterViewController onPreEngineRestart]
void onPreEngineRestart()
Definition: FlutterViewController.mm:477
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterThreadSynchronizer
Definition: FlutterThreadSynchronizer.h:16
flutter
Definition: AccessibilityBridgeMac.h:16
flutter::GetSwitchesFromEnvironment
std::vector< std::string > GetSwitchesFromEnvironment()
Definition: engine_switches.cc:14
FlutterAppDelegate
Definition: FlutterAppDelegate.h:54
engine_switches.h
_visible
BOOL _visible
Definition: FlutterEngine.mm:461
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
FlutterDisplayLink.h
FlutterAppDelegate.h
FlutterTimeConverter
Converts between the time representation used by Flutter Engine and CAMediaTime.
Definition: FlutterTimeConverter.h:13
kFlutterLifecycleChannel
NSString *const kFlutterLifecycleChannel
Definition: FlutterEngine.mm:35
_active
BOOL _active
Definition: FlutterEngine.mm:458
-[FlutterPlugin-p handleMethodCall:result:]
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
_binaryMessenger
FlutterBinaryMessengerRelay * _binaryMessenger
Definition: FlutterEngine.mm:464
+[FlutterMethodChannel methodChannelWithName:binaryMessenger:codec:]
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
-[FlutterTimeConverter CAMediaTimeToEngineTime:]
uint64_t CAMediaTimeToEngineTime:(CFTimeInterval time)
Definition: FlutterTimeConverter.mm:26
FlutterPlatformViewFactory-p
Definition: FlutterPlatformViews.h:13
_messengerHandlers
NSMutableDictionary< NSString *, FlutterEngineHandlerInfo * > * _messengerHandlers
Definition: FlutterEngine.mm:411
_aotData
_FlutterEngineAOTData * _aotData
Definition: FlutterEngine.mm:421
FlutterDartProject_Internal.h
FlutterViewController_Internal.h
_accessibilityChannel
FlutterBasicMessageChannel * _accessibilityChannel
Definition: FlutterEngine.mm:447
kFlutterSettingsChannel
NSString *const kFlutterSettingsChannel
Definition: FlutterEngine.mm:34
_compositor
FlutterCompositor _compositor
Definition: FlutterEngine.mm:433
-[FlutterPasteboard clearContents]
NSInteger clearContents()
Definition: FlutterEngine.mm:283
-[FlutterEngineTerminationHandler requestApplicationTermination:exitType:result:]
void requestApplicationTermination:exitType:result:(NSApplication *sender,[exitType] FlutterAppExitType type,[result] nullable FlutterResult result)
FlutterBinaryMessengerRelay
Definition: FlutterBinaryMessengerRelay.h:14
+[FlutterDisplayLink displayLinkWithView:]
instancetype displayLinkWithView:(NSView *view)
Definition: FlutterDisplayLink.mm:350
FlutterJSONMethodCodec
Definition: FlutterCodecs.h:455
+[FlutterDartProject lookupKeyForAsset:fromPackage:]
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
Definition: FlutterDartProject.mm:125
FlutterEngineRegistrar
Definition: FlutterEngine.mm:302
FlutterTexture-p
Definition: FlutterTexture.h:21
flutter::AppLifecycleState
AppLifecycleState
Definition: app_lifecycle_state.h:32
FlutterDartProject
Definition: FlutterDartProject.mm:24
FlutterEngineHandlerInfo
Definition: FlutterEngine.mm:63
_allowHeadlessExecution
BOOL _allowHeadlessExecution
Definition: FlutterEngine.mm:418
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:49
FlutterEngineTerminationHandler
Definition: FlutterEngine.mm:183
FlutterEngineHandlerInfo::connection
NSNumber * connection
Definition: FlutterEngine.mm:69
_flutterEngine
__weak FlutterEngine * _flutterEngine
Definition: FlutterEngine.mm:316
_connection
FlutterBinaryMessengerConnection _connection
Definition: FlutterChannels.mm:72
FlutterLocaleFromNSLocale
static FlutterLocale FlutterLocaleFromNSLocale(NSLocale *locale)
Definition: FlutterEngine.mm:41
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:469
FlutterBinaryMessengerConnection
int64_t FlutterBinaryMessengerConnection
Definition: FlutterBinaryMessenger.h:32
FlutterCompositor.h
_threadSynchronizer
FlutterThreadSynchronizer * _threadSynchronizer
Definition: FlutterEngine.mm:452
FlutterViewId
int64_t FlutterViewId
Definition: FlutterView.h:15
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
FlutterMethodCall::arguments
id arguments
Definition: FlutterCodecs.h:238
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()
_platformViewsChannel
FlutterMethodChannel * _platformViewsChannel
Definition: FlutterEngine.mm:437
FlutterJSONMessageCodec
Definition: FlutterCodecs.h:81
FlutterTerminationCallback
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)
OnPlatformMessage
static void OnPlatformMessage(const FlutterPlatformMessage *message, FlutterEngine *engine)
Definition: FlutterEngine.mm:397