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