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