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