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