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 <sstream>
11 #include <vector>
12 
13 #include "flutter/common/constants.h"
16 #include "flutter/shell/platform/embedder/embedder.h"
17 
18 #import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
20 #import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
35 
36 #import <CoreVideo/CoreVideo.h>
37 #import <IOSurface/IOSurface.h>
38 
40 
41 NSString* const kFlutterPlatformChannel = @"flutter/platform";
42 NSString* const kFlutterSettingsChannel = @"flutter/settings";
43 NSString* const kFlutterLifecycleChannel = @"flutter/lifecycle";
44 
45 using flutter::kFlutterImplicitViewId;
46 
47 /**
48  * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
49  * the returned struct.
50  */
51 static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
52  FlutterLocale flutterLocale = {};
53  flutterLocale.struct_size = sizeof(FlutterLocale);
54  flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
55  flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
56  flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
57  flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
58  return flutterLocale;
59 }
60 
61 /// The private notification for voice over.
62 static NSString* const kEnhancedUserInterfaceNotification =
63  @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
64 static NSString* const kEnhancedUserInterfaceKey = @"AXEnhancedUserInterface";
65 
66 /// Clipboard plain text format.
67 constexpr char kTextPlainFormat[] = "text/plain";
68 
69 #pragma mark -
70 
71 // Records an active handler of the messenger (FlutterEngine) that listens to
72 // platform messages on a given channel.
73 @interface FlutterEngineHandlerInfo : NSObject
74 
75 - (instancetype)initWithConnection:(NSNumber*)connection
76  handler:(FlutterBinaryMessageHandler)handler;
77 
78 @property(nonatomic, readonly) FlutterBinaryMessageHandler handler;
79 @property(nonatomic, readonly) NSNumber* connection;
80 
81 @end
82 
83 @implementation FlutterEngineHandlerInfo
84 - (instancetype)initWithConnection:(NSNumber*)connection
85  handler:(FlutterBinaryMessageHandler)handler {
86  self = [super init];
87  NSAssert(self, @"Super init cannot be nil");
89  _handler = handler;
90  return self;
91 }
92 @end
93 
94 #pragma mark -
95 
96 /**
97  * Private interface declaration for FlutterEngine.
98  */
103 
104 /**
105  * A mutable array that holds one bool value that determines if responses to platform messages are
106  * clear to execute. This value should be read or written only inside of a synchronized block and
107  * will return `NO` after the FlutterEngine has been dealloc'd.
108  */
109 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
110 
111 /**
112  * All delegates added via plugin calls to addApplicationDelegate.
113  */
114 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
115 
116 /**
117  * All registrars returned from registrarForPlugin:
118  */
119 @property(nonatomic, readonly)
120  NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
121 
122 - (nullable FlutterViewController*)viewControllerForIdentifier:
123  (FlutterViewIdentifier)viewIdentifier;
124 
125 /**
126  * An internal method that adds the view controller with the given ID.
127  *
128  * This method assigns the controller with the ID, puts the controller into the
129  * map, and does assertions related to the implicit view ID.
130  */
131 - (void)registerViewController:(FlutterViewController*)controller
132  forIdentifier:(FlutterViewIdentifier)viewIdentifier;
133 
134 /**
135  * An internal method that removes the view controller with the given ID.
136  *
137  * This method clears the ID of the controller, removes the controller from the
138  * map. This is an no-op if the view ID is not associated with any view
139  * controllers.
140  */
141 - (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier;
142 
143 /**
144  * Shuts down the engine if view requirement is not met, and headless execution
145  * is not allowed.
146  */
147 - (void)shutDownIfNeeded;
148 
149 /**
150  * Sends the list of user-preferred locales to the Flutter engine.
151  */
152 - (void)sendUserLocales;
153 
154 /**
155  * Handles a platform message from the engine.
156  */
157 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
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 - (NSViewController*)viewController {
366  return [_flutterEngine viewControllerForIdentifier:kFlutterImplicitViewId];
367 }
368 
369 - (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
370  channel:(nonnull FlutterMethodChannel*)channel {
371  [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
372  [delegate handleMethodCall:call result:result];
373  }];
374 }
375 
376 - (void)addApplicationDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
377  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
378  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
379  id<FlutterAppLifecycleProvider> lifeCycleProvider =
380  static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
381  [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
382  [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
383  }
384 }
385 
386 - (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
387  withId:(nonnull NSString*)factoryId {
388  [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
389 }
390 
391 - (void)publish:(NSObject*)value {
392  _publishedValue = value;
393 }
394 
395 - (NSString*)lookupKeyForAsset:(NSString*)asset {
396  return [FlutterDartProject lookupKeyForAsset:asset];
397 }
398 
399 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
400  return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
401 }
402 
403 @end
404 
405 // Callbacks provided to the engine. See the called methods for documentation.
406 #pragma mark - Static methods provided to engine configuration
407 
408 static void OnPlatformMessage(const FlutterPlatformMessage* message, void* user_data) {
409  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
410  [engine engineCallbackOnPlatformMessage:message];
411 }
412 
413 #pragma mark -
414 
415 @implementation FlutterEngine {
416  // The embedding-API-level engine object.
417  FLUTTER_API_SYMBOL(FlutterEngine) _engine;
418 
419  // The project being run by this engine.
421 
422  // A mapping of channel names to the registered information for those channels.
423  NSMutableDictionary<NSString*, FlutterEngineHandlerInfo*>* _messengerHandlers;
424 
425  // A self-incremental integer to assign to newly assigned channels as
426  // identification.
428 
429  // Whether the engine can continue running after the view controller is removed.
431 
432  // Pointer to the Dart AOT snapshot and instruction data.
433  _FlutterEngineAOTData* _aotData;
434 
435  // _macOSCompositor is created when the engine is created and its destruction is handled by ARC
436  // when the engine is destroyed.
437  std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
438 
439  // The information of all views attached to this engine mapped from IDs.
440  //
441  // It can't use NSDictionary, because the values need to be weak references.
442  NSMapTable* _viewControllers;
443 
444  // FlutterCompositor is copied and used in embedder.cc.
445  FlutterCompositor _compositor;
446 
447  // Method channel for platform view functions. These functions include creating, disposing and
448  // mutating a platform view.
450 
451  // Used to support creation and deletion of platform views and registering platform view
452  // factories. Lifecycle is tied to the engine.
454 
455  // Used to manage Flutter windows created by the Dart application
457 
458  // A message channel for sending user settings to the flutter engine.
460 
461  // A message channel for accessibility.
463 
464  // A method channel for miscellaneous platform functionality.
466 
467  // A method channel for taking screenshots via the rasterizer.
469 
470  // Whether the application is currently the active application.
471  BOOL _active;
472 
473  // Whether any portion of the application is currently visible.
474  BOOL _visible;
475 
476  // Proxy to allow plugins, channels to hold a weak reference to the binary messenger (self).
478 
479  // Map from ViewId to vsync waiter. Note that this is modified on main thread
480  // but accessed on UI thread, so access must be @synchronized.
481  NSMapTable<NSNumber*, FlutterVSyncWaiter*>* _vsyncWaiters;
482 
483  // Weak reference to last view that received a pointer event. This is used to
484  // pair cursor change with a view.
486 
487  // Pointer to a keyboard manager.
489 
490  // The text input plugin that handles text editing state for text fields.
492 
493  // Whether the engine is running in multi-window mode. This affects behavior
494  // when adding view controller (it will fail when calling multiple times without
495  // _multiviewEnabled).
497 
498  // View identifier for the next view to be created.
499  // Only used when multiview is enabled.
501 }
502 
503 @synthesize windowController = _windowController;
504 @synthesize project = _project;
505 
506 - (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
507  return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
508 }
509 
510 static const int kMainThreadPriority = 47;
511 
512 static void SetThreadPriority(FlutterThreadPriority priority) {
513  if (priority == kDisplay || priority == kRaster) {
514  pthread_t thread = pthread_self();
515  sched_param param;
516  int policy;
517  if (!pthread_getschedparam(thread, &policy, &param)) {
518  param.sched_priority = kMainThreadPriority;
519  pthread_setschedparam(thread, policy, &param);
520  }
521  pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
522  }
523 }
524 
525 - (instancetype)initWithName:(NSString*)labelPrefix
526  project:(FlutterDartProject*)project
527  allowHeadlessExecution:(BOOL)allowHeadlessExecution {
528  self = [super init];
529  NSAssert(self, @"Super init cannot be nil");
530 
531  [FlutterRunLoop ensureMainLoopInitialized];
532 
533  _pasteboard = [[FlutterPasteboard alloc] init];
534  _active = NO;
535  _visible = NO;
536  _project = project ?: [[FlutterDartProject alloc] init];
537  _messengerHandlers = [[NSMutableDictionary alloc] init];
538  _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
539  _pluginRegistrars = [[NSMutableDictionary alloc] init];
541  _allowHeadlessExecution = allowHeadlessExecution;
542  _semanticsEnabled = NO;
543  _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
544  _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
545  [_isResponseValid addObject:@YES];
546  _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self];
547  _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
548  _multiViewEnabled = NO;
550 
551  _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
552  FlutterEngineGetProcAddresses(&_embedderAPI);
553 
554  _viewControllers = [NSMapTable weakToWeakObjectsMapTable];
555  _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self];
556 
557  NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
558  [notificationCenter addObserver:self
559  selector:@selector(sendUserLocales)
560  name:NSCurrentLocaleDidChangeNotification
561  object:nil];
562 
564  // The macOS compositor must be initialized in the initializer because it is
565  // used when adding views, which might happen before runWithEntrypoint.
566  _macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
567  [[FlutterViewEngineProvider alloc] initWithEngine:self],
568  [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController);
569 
570  [self setUpPlatformViewChannel];
571 
573  _windowController.engine = self;
574 
575  [self setUpAccessibilityChannel];
576  [self setUpNotificationCenterListeners];
577  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
578  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
579  _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self
580  terminator:nil];
581  id<FlutterAppLifecycleProvider> lifecycleProvider =
582  static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
583  [lifecycleProvider addApplicationLifecycleDelegate:self];
584  } else {
585  _terminationHandler = nil;
586  }
587 
588  _vsyncWaiters = [NSMapTable strongToStrongObjectsMapTable];
589 
590  return self;
591 }
592 
593 - (void)dealloc {
594  id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
595  if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
596  id<FlutterAppLifecycleProvider> lifecycleProvider =
597  static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
598  [lifecycleProvider removeApplicationLifecycleDelegate:self];
599 
600  // Unregister any plugins that registered as app delegates, since they are not guaranteed to
601  // live after the engine is destroyed, and their delegation registration is intended to be bound
602  // to the engine and its lifetime.
603  for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
604  if (delegate) {
605  [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
606  }
607  }
608  }
609  // Clear any published values, just in case a plugin has created a retain cycle with the
610  // registrar.
611  for (NSString* pluginName in _pluginRegistrars) {
612  [_pluginRegistrars[pluginName] publish:[NSNull null]];
613  }
614  @synchronized(_isResponseValid) {
615  [_isResponseValid removeAllObjects];
616  [_isResponseValid addObject:@NO];
617  }
618  [self shutDownEngine];
619  if (_aotData) {
620  _embedderAPI.CollectAOTData(_aotData);
621  }
622 }
623 
624 - (FlutterTaskRunnerDescription)createPlatformThreadTaskDescription {
625  static size_t sTaskRunnerIdentifiers = 0;
626  FlutterTaskRunnerDescription cocoa_task_runner_description = {
627  .struct_size = sizeof(FlutterTaskRunnerDescription),
628  // Retain for use in post_task_callback. Released in destruction_callback.
629  .user_data = (__bridge_retained void*)self,
630  .runs_task_on_current_thread_callback = [](void* user_data) -> bool {
631  return [[NSThread currentThread] isMainThread];
632  },
633  .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
634  void* user_data) -> void {
635  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
636  [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
637  },
638  .identifier = ++sTaskRunnerIdentifiers,
639  .destruction_callback =
640  [](void* user_data) {
641  // Balancing release for the retain when setting user_data above.
642  FlutterEngine* engine = (__bridge_transfer FlutterEngine*)user_data;
643  engine = nil;
644  },
645  };
646  return cocoa_task_runner_description;
647 }
648 
649 - (void)onFocusChangeRequest:(const FlutterViewFocusChangeRequest*)request {
650  FlutterViewController* controller = [self viewControllerForIdentifier:request->view_id];
651  if (controller == nil) {
652  return;
653  }
654  if (request->state == kFocused) {
655  [controller.flutterView.window makeFirstResponder:controller.flutterView];
656  }
657 }
658 
659 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
660  if (self.running) {
661  return NO;
662  }
663 
664  if (!_allowHeadlessExecution && [_viewControllers count] == 0) {
665  NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
666  return NO;
667  }
668 
669  [self addInternalPlugins];
670 
671  // The first argument of argv is required to be the executable name.
672  std::vector<const char*> argv = {[self.executableName UTF8String]};
673  std::vector<std::string> switches = self.switches;
674 
675  // Enable Impeller only if specifically asked for from the project or cmdline arguments.
676  if (_project.enableImpeller ||
677  std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end()) {
678  switches.push_back("--enable-impeller=true");
679  }
680 
681  if (_project.enableSDFs ||
682  std::find(switches.begin(), switches.end(), "--impeller-use-sdfs=true") != switches.end()) {
683  switches.push_back("--impeller-use-sdfs=true");
684  }
685 
686  if (_project.enableFlutterGPU ||
687  std::find(switches.begin(), switches.end(), "--enable-flutter-gpu=true") != switches.end()) {
688  switches.push_back("--enable-flutter-gpu=true");
689  }
690 
691  std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
692  [](const std::string& arg) -> const char* { return arg.c_str(); });
693 
694  std::vector<const char*> dartEntrypointArgs;
695  for (NSString* argument in [_project dartEntrypointArguments]) {
696  dartEntrypointArgs.push_back([argument UTF8String]);
697  }
698 
699  FlutterProjectArgs flutterArguments = {};
700  flutterArguments.struct_size = sizeof(FlutterProjectArgs);
701  flutterArguments.assets_path = _project.assetsPath.UTF8String;
702  flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String;
703  flutterArguments.command_line_argc = static_cast<int>(argv.size());
704  flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
705  flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
706  flutterArguments.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
707  void* user_data) {
708  // TODO(dkwingsmt): This callback only supports single-view, therefore it
709  // only operates on the implicit view. To support multi-view, we need a
710  // way to pass in the ID (probably through FlutterSemanticsUpdate).
711  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
712  [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
713  };
714  flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
715  flutterArguments.shutdown_dart_vm_when_done = true;
716  flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
717  flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
718  flutterArguments.root_isolate_create_callback = _project.rootIsolateCreateCallback;
719  flutterArguments.log_message_callback = [](const char* tag, const char* message,
720  void* user_data) {
721  std::stringstream stream;
722  if (tag && tag[0]) {
723  stream << tag << ": ";
724  }
725  stream << message;
726  std::string log = stream.str();
727  [FlutterLogger logDirect:[NSString stringWithUTF8String:log.c_str()]];
728  };
729 
730  flutterArguments.engine_id = reinterpret_cast<int64_t>((__bridge void*)self);
731  flutterArguments.enable_wide_gamut = _project.enableWideGamut;
732 
733  BOOL mergedPlatformUIThread = YES;
734  NSNumber* enableMergedPlatformUIThread =
735  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
736  if (enableMergedPlatformUIThread != nil) {
737  mergedPlatformUIThread = enableMergedPlatformUIThread.boolValue;
738  }
739 
740  if (mergedPlatformUIThread) {
741  NSLog(@"Running with merged UI and platform thread. Experimental.");
742  }
743 
744  // The task description needs to be created separately for platform task
745  // runner and UI task runner because each one has their own __bridge_retained
746  // engine user data.
747  FlutterTaskRunnerDescription platformTaskRunnerDescription =
748  [self createPlatformThreadTaskDescription];
749  std::optional<FlutterTaskRunnerDescription> uiTaskRunnerDescription;
750  if (mergedPlatformUIThread) {
751  uiTaskRunnerDescription = [self createPlatformThreadTaskDescription];
752  }
753 
754  const FlutterCustomTaskRunners custom_task_runners = {
755  .struct_size = sizeof(FlutterCustomTaskRunners),
756  .platform_task_runner = &platformTaskRunnerDescription,
757  .thread_priority_setter = SetThreadPriority,
758  .ui_task_runner = uiTaskRunnerDescription ? &uiTaskRunnerDescription.value() : nullptr,
759  };
760  flutterArguments.custom_task_runners = &custom_task_runners;
761 
762  [self loadAOTData:_project.assetsPath];
763  if (_aotData) {
764  flutterArguments.aot_data = _aotData;
765  }
766 
767  flutterArguments.compositor = [self createFlutterCompositor];
768 
769  flutterArguments.on_pre_engine_restart_callback = [](void* user_data) {
770  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
771  [engine engineCallbackOnPreEngineRestart];
772  };
773 
774  flutterArguments.vsync_callback = [](void* user_data, intptr_t baton) {
775  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
776  [engine onVSync:baton];
777  };
778 
779  flutterArguments.view_focus_change_request_callback =
780  [](const FlutterViewFocusChangeRequest* request, void* user_data) {
781  FlutterEngine* engine = (__bridge FlutterEngine*)user_data;
782  [engine onFocusChangeRequest:request];
783  };
784 
785  FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
786  FlutterEngineResult result = _embedderAPI.Initialize(
787  FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
788  if (result != kSuccess) {
789  NSLog(@"Failed to initialize Flutter engine: error %d", result);
790  return NO;
791  }
792 
793  result = _embedderAPI.RunInitialized(_engine);
794  if (result != kSuccess) {
795  NSLog(@"Failed to run an initialized engine: error %d", result);
796  return NO;
797  }
798 
799  [self sendUserLocales];
800 
801  // Update window metric for all view controllers.
802  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
803  FlutterViewController* nextViewController;
804  while ((nextViewController = [viewControllerEnumerator nextObject])) {
805  [self updateWindowMetricsForViewController:nextViewController];
806  }
807 
808  [self updateDisplayConfig];
809  // Send the initial user settings such as brightness and text scale factor
810  // to the engine.
811  [self sendInitialSettings];
812  return YES;
813 }
814 
815 - (void)loadAOTData:(NSString*)assetsDir {
816  if (!_embedderAPI.RunsAOTCompiledDartCode()) {
817  return;
818  }
819 
820  BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath.
821  NSFileManager* fileManager = [NSFileManager defaultManager];
822 
823  // This is the location where the test fixture places the snapshot file.
824  // For applications built by Flutter tool, this is in "App.framework".
825  NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
826 
827  if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
828  return;
829  }
830 
831  FlutterEngineAOTDataSource source = {};
832  source.type = kFlutterEngineAOTDataSourceTypeElfPath;
833  source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
834 
835  auto result = _embedderAPI.CreateAOTData(&source, &_aotData);
836  if (result != kSuccess) {
837  NSLog(@"Failed to load AOT data from: %@", elfPath);
838  }
839 }
840 
841 - (void)registerViewController:(FlutterViewController*)controller
842  forIdentifier:(FlutterViewIdentifier)viewIdentifier {
843  _macOSCompositor->AddView(viewIdentifier);
844  NSAssert(controller != nil, @"The controller must not be nil.");
845  if (!_multiViewEnabled) {
846  NSAssert(controller.engine == nil,
847  @"The FlutterViewController is unexpectedly attached to "
848  @"engine %@ before initialization.",
849  controller.engine);
850  }
851  NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil,
852  @"The requested view ID is occupied.");
853  [_viewControllers setObject:controller forKey:@(viewIdentifier)];
854  [controller setUpWithEngine:self viewIdentifier:viewIdentifier];
855  NSAssert(controller.viewIdentifier == viewIdentifier, @"Failed to assign view ID.");
856  // Verify that the controller's property are updated accordingly. Failing the
857  // assertions is likely because either the FlutterViewController or the
858  // FlutterEngine is mocked. Please subclass these classes instead.
859  NSAssert(controller.attached, @"The FlutterViewController should switch to the attached mode "
860  @"after it is added to a FlutterEngine.");
861  NSAssert(controller.engine == self,
862  @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
863  self, controller.engine);
864 
865  if (controller.viewLoaded) {
866  [self viewControllerViewDidLoad:controller];
867  }
868 
869  if (viewIdentifier != kFlutterImplicitViewId) {
870  // These will be overriden immediately after the FlutterView is created
871  // by actual values.
872  FlutterWindowMetricsEvent metrics{
873  .struct_size = sizeof(FlutterWindowMetricsEvent),
874  .width = 0,
875  .height = 0,
876  .pixel_ratio = 1.0,
877  };
878  bool added = false;
879  FlutterAddViewInfo info{.struct_size = sizeof(FlutterAddViewInfo),
880  .view_id = viewIdentifier,
881  .view_metrics = &metrics,
882  .user_data = &added,
883  .add_view_callback = [](const FlutterAddViewResult* r) {
884  auto added = reinterpret_cast<bool*>(r->user_data);
885  *added = true;
886  }};
887  // The callback should be called synchronously from platform thread.
888  _embedderAPI.AddView(_engine, &info);
889  FML_DCHECK(added);
890  if (!added) {
891  NSLog(@"Failed to add view with ID %llu", viewIdentifier);
892  }
893  }
894 }
895 
896 - (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
897  __weak FlutterEngine* weakSelf = self;
898  FlutterTimeConverter* timeConverter = [[FlutterTimeConverter alloc] initWithEngine:self];
899  FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
900  initWithDisplayLink:[FlutterDisplayLink displayLinkWithView:viewController.view]
901  block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
902  uintptr_t baton) {
903  uint64_t timeNanos = [timeConverter CAMediaTimeToEngineTime:timestamp];
904  uint64_t targetTimeNanos =
905  [timeConverter CAMediaTimeToEngineTime:targetTimestamp];
906  FlutterEngine* engine = weakSelf;
907  if (engine) {
908  engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
909  }
910  }];
911  @synchronized(_vsyncWaiters) {
912  FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil);
913  [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
914  }
915 }
916 
917 - (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
918  if (viewIdentifier != kFlutterImplicitViewId) {
919  bool removed = false;
920  FlutterRemoveViewInfo info;
921  info.struct_size = sizeof(FlutterRemoveViewInfo);
922  info.view_id = viewIdentifier;
923  info.user_data = &removed;
924  // RemoveViewCallback is not finished synchronously, the remove_view_callback
925  // is called from raster thread when the engine knows for sure that the resources
926  // associated with the view are no longer needed.
927  info.remove_view_callback = [](const FlutterRemoveViewResult* r) {
928  auto removed = reinterpret_cast<bool*>(r->user_data);
929  [FlutterRunLoop.mainRunLoop performBlock:^{
930  *removed = true;
931  }];
932  };
933  _embedderAPI.RemoveView(_engine, &info);
934  while (!removed) {
935  [[FlutterRunLoop mainRunLoop] pollFlutterMessagesOnce];
936  }
937  }
938 
939  _macOSCompositor->RemoveView(viewIdentifier);
940 
941  FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier];
942  // The controller can be nil. The engine stores only a weak ref, and this
943  // method could have been called from the controller's dealloc.
944  if (controller != nil) {
945  [controller detachFromEngine];
946  NSAssert(!controller.attached,
947  @"The FlutterViewController unexpectedly stays attached after being removed. "
948  @"In unit tests, this is likely because either the FlutterViewController or "
949  @"the FlutterEngine is mocked. Please subclass these classes instead.");
950  }
951  [_viewControllers removeObjectForKey:@(viewIdentifier)];
952 
953  FlutterVSyncWaiter* waiter = nil;
954  @synchronized(_vsyncWaiters) {
955  waiter = [_vsyncWaiters objectForKey:@(viewIdentifier)];
956  [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
957  }
958  [waiter invalidate];
959 }
960 
961 - (void)shutDownIfNeeded {
962  if ([_viewControllers count] == 0 && !_allowHeadlessExecution) {
963  [self shutDownEngine];
964  }
965 }
966 
967 - (FlutterViewController*)viewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
968  FlutterViewController* controller = [_viewControllers objectForKey:@(viewIdentifier)];
969  NSAssert(controller == nil || controller.viewIdentifier == viewIdentifier,
970  @"The stored controller has unexpected view ID.");
971  return controller;
972 }
973 
974 - (void)setViewController:(FlutterViewController*)controller {
975  FlutterViewController* currentController =
976  [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
977  if (currentController == controller) {
978  // From nil to nil, or from non-nil to the same controller.
979  return;
980  }
981  if (currentController == nil && controller != nil) {
982  // From nil to non-nil.
983  NSAssert(controller.engine == nil,
984  @"Failed to set view controller to the engine: "
985  @"The given FlutterViewController is already attached to an engine %@. "
986  @"If you wanted to create an FlutterViewController and set it to an existing engine, "
987  @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
988  controller.engine);
989  [self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
990  } else if (currentController != nil && controller == nil) {
991  NSAssert(currentController.viewIdentifier == kFlutterImplicitViewId,
992  @"The default controller has an unexpected ID %llu", currentController.viewIdentifier);
993  // From non-nil to nil.
994  [self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
995  [self shutDownIfNeeded];
996  } else {
997  // From non-nil to a different non-nil view controller.
998  NSAssert(NO,
999  @"Failed to set view controller to the engine: "
1000  @"The engine already has an implicit view controller %@. "
1001  @"If you wanted to make the implicit view render in a different window, "
1002  @"you should attach the current view controller to the window instead.",
1003  [_viewControllers objectForKey:@(kFlutterImplicitViewId)]);
1004  }
1005 }
1006 
1007 - (FlutterViewController*)viewController {
1008  return [self viewControllerForIdentifier:kFlutterImplicitViewId];
1009 }
1010 
1011 - (FlutterCompositor*)createFlutterCompositor {
1012  _compositor = {};
1013  _compositor.struct_size = sizeof(FlutterCompositor);
1014  _compositor.user_data = _macOSCompositor.get();
1015 
1016  _compositor.create_backing_store_callback = [](const FlutterBackingStoreConfig* config, //
1017  FlutterBackingStore* backing_store_out, //
1018  void* user_data //
1019  ) {
1020  return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
1021  config, backing_store_out);
1022  };
1023 
1024  _compositor.collect_backing_store_callback = [](const FlutterBackingStore* backing_store, //
1025  void* user_data //
1026  ) { return true; };
1027 
1028  _compositor.present_view_callback = [](const FlutterPresentViewInfo* info) {
1029  return reinterpret_cast<flutter::FlutterCompositor*>(info->user_data)
1030  ->Present(info->view_id, info->layers, info->layers_count);
1031  };
1032 
1033  _compositor.avoid_backing_store_cache = true;
1034 
1035  return &_compositor;
1036 }
1037 
1038 - (id<FlutterBinaryMessenger>)binaryMessenger {
1039  return _binaryMessenger;
1040 }
1041 
1042 #pragma mark - Framework-internal methods
1043 
1044 - (void)addViewController:(FlutterViewController*)controller {
1045  if (!_multiViewEnabled) {
1046  // When multiview is disabled, the engine will only assign views to the implicit view ID.
1047  // The implicit view ID can be reused if and only if the implicit view is unassigned.
1048  NSAssert(self.viewController == nil,
1049  @"The engine already has a view controller for the implicit view.");
1050  self.viewController = controller;
1051  } else {
1052  // When multiview is enabled, the engine will assign views to a self-incrementing ID.
1053  // The implicit view ID can not be reused.
1054  FlutterViewIdentifier viewIdentifier = _nextViewIdentifier++;
1055  [self registerViewController:controller forIdentifier:viewIdentifier];
1056  }
1057 }
1058 
1059 - (void)enableMultiView {
1060  if (!_multiViewEnabled) {
1061  NSAssert(self.viewController == nil,
1062  @"Multiview can only be enabled before adding any view controllers.");
1063  _multiViewEnabled = YES;
1064  }
1065 }
1066 
1067 - (void)windowDidBecomeKey:(FlutterViewIdentifier)viewIdentifier {
1068  FlutterViewFocusEvent event{
1069  .struct_size = sizeof(FlutterViewFocusEvent),
1070  .view_id = viewIdentifier,
1071  .state = kFocused,
1072  .direction = kUndefined,
1073  };
1074  _embedderAPI.SendViewFocusEvent(_engine, &event);
1075 }
1076 
1077 - (void)windowDidResignKey:(FlutterViewIdentifier)viewIdentifier {
1078  FlutterViewFocusEvent event{
1079  .struct_size = sizeof(FlutterViewFocusEvent),
1080  .view_id = viewIdentifier,
1081  .state = kUnfocused,
1082  .direction = kUndefined,
1083  };
1084  _embedderAPI.SendViewFocusEvent(_engine, &event);
1085 }
1086 
1087 - (void)removeViewController:(nonnull FlutterViewController*)viewController {
1088  [self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
1089  [self shutDownIfNeeded];
1090 }
1091 
1092 - (BOOL)running {
1093  return _engine != nullptr;
1094 }
1095 
1096 - (void)updateDisplayConfig:(NSNotification*)notification {
1097  [self updateDisplayConfig];
1098 }
1099 
1100 - (NSArray<NSScreen*>*)screens {
1101  return [NSScreen screens];
1102 }
1103 
1104 - (void)updateDisplayConfig {
1105  if (!_engine) {
1106  return;
1107  }
1108 
1109  std::vector<FlutterEngineDisplay> displays;
1110  for (NSScreen* screen : [self screens]) {
1111  CGDirectDisplayID displayID =
1112  static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
1113 
1114  double devicePixelRatio = screen.backingScaleFactor;
1115  FlutterEngineDisplay display;
1116  display.struct_size = sizeof(display);
1117  display.display_id = displayID;
1118  display.single_display = false;
1119  display.width = static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
1120  display.height = static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
1121  display.device_pixel_ratio = devicePixelRatio;
1122 
1123  CVDisplayLinkRef displayLinkRef = nil;
1124  CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
1125 
1126  if (error == 0) {
1127  CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
1128  if (!(nominal.flags & kCVTimeIsIndefinite)) {
1129  double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
1130  display.refresh_rate = round(refreshRate);
1131  }
1132  CVDisplayLinkRelease(displayLinkRef);
1133  } else {
1134  display.refresh_rate = 0;
1135  }
1136 
1137  displays.push_back(display);
1138  }
1139  _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
1140  displays.data(), displays.size());
1141 }
1142 
1143 - (void)onSettingsChanged:(NSNotification*)notification {
1144  // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
1145  NSString* brightness =
1146  [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
1147  [_settingsChannel sendMessage:@{
1148  @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
1149  // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
1150  @"textScaleFactor" : @1.0,
1151  @"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]),
1152  }];
1153 }
1154 
1155 - (void)sendInitialSettings {
1156  // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
1157  [[NSDistributedNotificationCenter defaultCenter]
1158  addObserver:self
1159  selector:@selector(onSettingsChanged:)
1160  name:@"AppleInterfaceThemeChangedNotification"
1161  object:nil];
1162  [self onSettingsChanged:nil];
1163 }
1164 
1165 - (FlutterEngineProcTable&)embedderAPI {
1166  return _embedderAPI;
1167 }
1168 
1169 - (nonnull NSString*)executableName {
1170  return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter";
1171 }
1172 
1173 - (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
1174  if (!_engine || !viewController || !viewController.viewLoaded) {
1175  return;
1176  }
1177  NSAssert([self viewControllerForIdentifier:viewController.viewIdentifier] == viewController,
1178  @"The provided view controller is not attached to this engine.");
1179  FlutterView* view = viewController.flutterView;
1180  CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1181  CGSize scaledSize = scaledBounds.size;
1182  double pixelRatio = view.layer.contentsScale;
1183  auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1184  FlutterWindowMetricsEvent windowMetricsEvent = {
1185  .struct_size = sizeof(windowMetricsEvent),
1186  .width = static_cast<size_t>(scaledSize.width),
1187  .height = static_cast<size_t>(scaledSize.height),
1188  .pixel_ratio = pixelRatio,
1189  .left = static_cast<size_t>(scaledBounds.origin.x),
1190  .top = static_cast<size_t>(scaledBounds.origin.y),
1191  .display_id = static_cast<uint64_t>(displayId),
1192  .view_id = viewController.viewIdentifier,
1193  };
1194  if (view.sizedToContents) {
1195  CGSize maximumContentSize = [view convertSizeToBacking:view.maximumContentSize];
1196  CGSize minimumContentSize = [view convertSizeToBacking:view.minimumContentSize];
1197  windowMetricsEvent.has_constraints = true;
1198  windowMetricsEvent.min_width_constraint = static_cast<size_t>(minimumContentSize.width);
1199  windowMetricsEvent.min_height_constraint = static_cast<size_t>(minimumContentSize.height);
1200  windowMetricsEvent.max_width_constraint = static_cast<size_t>(maximumContentSize.width);
1201  windowMetricsEvent.max_height_constraint = static_cast<size_t>(maximumContentSize.height);
1202  } else {
1203  windowMetricsEvent.min_width_constraint = static_cast<size_t>(scaledSize.width);
1204  windowMetricsEvent.min_height_constraint = static_cast<size_t>(scaledSize.height);
1205  windowMetricsEvent.max_width_constraint = static_cast<size_t>(scaledSize.width);
1206  windowMetricsEvent.max_height_constraint = static_cast<size_t>(scaledSize.height);
1207  }
1208  _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1209 }
1210 
1211 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1212  _embedderAPI.SendPointerEvent(_engine, &event, 1);
1213  _lastViewWithPointerEvent = [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView;
1214 }
1215 
1216 - (void)setSemanticsEnabled:(BOOL)enabled {
1217  if (_semanticsEnabled == enabled) {
1218  return;
1219  }
1220  _semanticsEnabled = enabled;
1221 
1222  // Update all view controllers' bridges.
1223  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1224  FlutterViewController* nextViewController;
1225  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1226  [nextViewController notifySemanticsEnabledChanged];
1227  }
1228 
1229  _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1230 }
1231 
1232 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1233  toTarget:(uint16_t)target
1234  withData:(fml::MallocMapping)data {
1235  _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1236 }
1237 
1238 - (FlutterPlatformViewController*)platformViewController {
1239  return _platformViewController;
1240 }
1241 
1242 #pragma mark - Private methods
1243 
1244 - (void)sendUserLocales {
1245  if (!self.running) {
1246  return;
1247  }
1248 
1249  // Create a list of FlutterLocales corresponding to the preferred languages.
1250  NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1251  std::vector<FlutterLocale> flutterLocales;
1252  flutterLocales.reserve(locales.count);
1253  for (NSString* localeID in [NSLocale preferredLanguages]) {
1254  NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1255  [locales addObject:locale];
1256  flutterLocales.push_back(FlutterLocaleFromNSLocale(locale));
1257  }
1258  // Convert to a list of pointers, and send to the engine.
1259  std::vector<const FlutterLocale*> flutterLocaleList;
1260  flutterLocaleList.reserve(flutterLocales.size());
1261  std::transform(flutterLocales.begin(), flutterLocales.end(),
1262  std::back_inserter(flutterLocaleList),
1263  [](const auto& arg) -> const auto* { return &arg; });
1264  _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1265 }
1266 
1267 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1268  NSData* messageData = nil;
1269  if (message->message_size > 0) {
1270  messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1271  length:message->message_size
1272  freeWhenDone:NO];
1273  }
1274  NSString* channel = @(message->channel);
1275  __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1276  __block FlutterEngine* weakSelf = self;
1277  NSMutableArray* isResponseValid = self.isResponseValid;
1278  FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1279  _embedderAPI.SendPlatformMessageResponse;
1280  FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
1281  @synchronized(isResponseValid) {
1282  if (![isResponseValid[0] boolValue]) {
1283  // Ignore, engine was killed.
1284  return;
1285  }
1286  if (responseHandle) {
1287  sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1288  static_cast<const uint8_t*>(response.bytes), response.length);
1289  responseHandle = NULL;
1290  } else {
1291  NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
1292  "on channel '%@'.",
1293  channel);
1294  }
1295  }
1296  };
1297 
1298  FlutterEngineHandlerInfo* handlerInfo = _messengerHandlers[channel];
1299  if (handlerInfo) {
1300  handlerInfo.handler(messageData, binaryResponseHandler);
1301  } else {
1302  binaryResponseHandler(nil);
1303  }
1304 }
1305 
1306 - (void)engineCallbackOnPreEngineRestart {
1307  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1308  FlutterViewController* nextViewController;
1309  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1310  [nextViewController onPreEngineRestart];
1311  }
1312  [_windowController closeAllWindows];
1313  [_platformViewController reset];
1314  _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self];
1315 }
1316 
1317 // This will be called on UI thread, which maybe or may not be platform thread,
1318 // depending on the configuration.
1319 - (void)onVSync:(uintptr_t)baton {
1320  auto block = ^{
1321  // TODO(knopp): Use vsync waiter for correct view.
1322  // https://github.com/flutter/flutter/issues/142845
1323  FlutterVSyncWaiter* waiter =
1324  [_vsyncWaiters objectForKey:[_vsyncWaiters.keyEnumerator nextObject]];
1325  if (waiter != nil) {
1326  [waiter waitForVSync:baton];
1327  } else {
1328  // Sometimes there is a vsync request right after the last view is removed.
1329  // It still need to be handled, otherwise the engine will stop producing frames
1330  // even if a new view is added later.
1331  self.embedderAPI.OnVsync(_engine, baton, 0, 0);
1332  }
1333  };
1334  if ([NSThread isMainThread]) {
1335  block();
1336  } else {
1337  [FlutterRunLoop.mainRunLoop performBlock:block];
1338  }
1339 }
1340 
1341 /**
1342  * Note: Called from dealloc. Should not use accessors or other methods.
1343  */
1344 - (void)shutDownEngine {
1345  if (_engine == nullptr) {
1346  return;
1347  }
1348 
1349  FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1350  if (result != kSuccess) {
1351  NSLog(@"Could not de-initialize the Flutter engine: error %d", result);
1352  }
1353 
1354  result = _embedderAPI.Shutdown(_engine);
1355  if (result != kSuccess) {
1356  NSLog(@"Failed to shut down Flutter engine: error %d", result);
1357  }
1358  _engine = nullptr;
1359 }
1360 
1361 + (FlutterEngine*)engineForIdentifier:(int64_t)identifier {
1362  NSAssert([[NSThread currentThread] isMainThread], @"Must be called on the main thread.");
1363  return (__bridge FlutterEngine*)reinterpret_cast<void*>(identifier);
1364 }
1365 
1366 - (void)setUpPlatformViewChannel {
1368  [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
1369  binaryMessenger:self.binaryMessenger
1370  codec:[FlutterStandardMethodCodec sharedInstance]];
1371 
1372  __weak FlutterEngine* weakSelf = self;
1373  [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1374  [[weakSelf platformViewController] handleMethodCall:call result:result];
1375  }];
1376 }
1377 
1378 - (void)setUpAccessibilityChannel {
1380  messageChannelWithName:@"flutter/accessibility"
1381  binaryMessenger:self.binaryMessenger
1383  __weak FlutterEngine* weakSelf = self;
1384  [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1385  [weakSelf handleAccessibilityEvent:message];
1386  }];
1387 }
1388 - (void)setUpNotificationCenterListeners {
1389  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1390  // macOS fires this private message when VoiceOver turns on or off.
1391  [center addObserver:self
1392  selector:@selector(onAccessibilityStatusChanged:)
1393  name:kEnhancedUserInterfaceNotification
1394  object:nil];
1395  [center addObserver:self
1396  selector:@selector(applicationWillTerminate:)
1397  name:NSApplicationWillTerminateNotification
1398  object:nil];
1399  [center addObserver:self
1400  selector:@selector(windowDidChangeScreen:)
1401  name:NSWindowDidChangeScreenNotification
1402  object:nil];
1403  [center addObserver:self
1404  selector:@selector(updateDisplayConfig:)
1405  name:NSApplicationDidChangeScreenParametersNotification
1406  object:nil];
1407 }
1408 
1409 - (void)addInternalPlugins {
1410  __weak FlutterEngine* weakSelf = self;
1411  [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]
1412  delegate:self];
1413  [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
1414 
1416  [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel
1417  binaryMessenger:self.binaryMessenger
1420  [FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel
1421  binaryMessenger:self.binaryMessenger
1422  codec:[FlutterJSONMethodCodec sharedInstance]];
1423  [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1424  [weakSelf handleMethodCall:call result:result];
1425  }];
1426 
1428  [FlutterMethodChannel methodChannelWithName:@"flutter/screenshot"
1429  binaryMessenger:self.binaryMessenger
1430  codec:[FlutterStandardMethodCodec sharedInstance]];
1431  [_screenshotChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1432  FlutterEngine* strongSelf = weakSelf;
1433  if (!strongSelf) {
1434  return result([FlutterError errorWithCode:@"invalid_state"
1435  message:@"Engine deallocated."
1436  details:nil]);
1437  }
1438 
1439  FlutterViewController* viewController =
1440  [strongSelf viewControllerForIdentifier:flutter::kFlutterImplicitViewId];
1441  if (!viewController) {
1442  return result([FlutterError errorWithCode:@"failure"
1443  message:@"No view controller."
1444  details:nil]);
1445  }
1446 
1447  NSArray<FlutterSurface*>* frontSurfaces =
1448  viewController.flutterView.surfaceManager.frontSurfaces;
1449  if (frontSurfaces.count == 0) {
1450  return result([FlutterError errorWithCode:@"failure"
1451  message:@"No front surfaces."
1452  details:nil]);
1453  }
1454 
1455  // Use the first front surface (the main backing store).
1456  FlutterSurface* surface = frontSurfaces.firstObject;
1457  IOSurfaceRef ioSurface = surface.ioSurface;
1458 
1459  size_t width = IOSurfaceGetWidth(ioSurface);
1460  size_t height = IOSurfaceGetHeight(ioSurface);
1461  size_t bytesPerRow = IOSurfaceGetBytesPerRow(ioSurface);
1462  size_t bytesPerElement = IOSurfaceGetBytesPerElement(ioSurface);
1463  uint32_t pixelFormat = (uint32_t)IOSurfaceGetPixelFormat(ioSurface);
1464 
1465  NSString* formatString;
1466  switch (pixelFormat) {
1467  case kCVPixelFormatType_40ARGBLEWideGamut:
1468  formatString = @"MTLPixelFormatBGRA10_XR";
1469  break;
1470  case kCVPixelFormatType_32BGRA:
1471  formatString = @"MTLPixelFormatBGRA8Unorm";
1472  break;
1473  default:
1474  formatString = [NSString stringWithFormat:@"Unknown(%u)", pixelFormat];
1475  break;
1476  }
1477 
1478  IOSurfaceLock(ioSurface, kIOSurfaceLockReadOnly, nil);
1479  void* baseAddress = IOSurfaceGetBaseAddress(ioSurface);
1480 
1481  // Copy pixel data row by row into a tightly-packed buffer.
1482  size_t packedBytesPerRow = width * bytesPerElement;
1483  NSMutableData* packedData = [NSMutableData dataWithLength:packedBytesPerRow * height];
1484  uint8_t* dest = (uint8_t*)packedData.mutableBytes;
1485  for (size_t row = 0; row < height; row++) {
1486  memcpy(dest + row * packedBytesPerRow, (uint8_t*)baseAddress + row * bytesPerRow,
1487  packedBytesPerRow);
1488  }
1489 
1490  IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nil);
1491 
1492  return result(@[
1493  @(width),
1494  @(height),
1495  formatString,
1497  ]);
1498  }];
1499 }
1500 
1501 - (void)didUpdateMouseCursor:(NSCursor*)cursor {
1502  // Mouse cursor plugin does not specify which view is responsible for changing the cursor,
1503  // so the reasonable assumption here is that cursor change is a result of a mouse movement
1504  // and thus the cursor will be paired with last Flutter view that reveived mouse event.
1505  [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1506 }
1507 
1508 - (void)applicationWillTerminate:(NSNotification*)notification {
1509  [self shutDownEngine];
1510 }
1511 
1512 - (void)windowDidChangeScreen:(NSNotification*)notification {
1513  // Update window metric for all view controllers since the display_id has
1514  // changed.
1515  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1516  FlutterViewController* nextViewController;
1517  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1518  [self updateWindowMetricsForViewController:nextViewController];
1519  [nextViewController updateWideGamutForScreen];
1520  }
1521 }
1522 
1523 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1524  BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1525  NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1526  FlutterViewController* nextViewController;
1527  while ((nextViewController = [viewControllerEnumerator nextObject])) {
1528  [nextViewController onAccessibilityStatusChanged:enabled];
1529  }
1530 
1531  self.semanticsEnabled = enabled;
1532 }
1533 - (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
1534  NSString* type = annotatedEvent[@"type"];
1535  if ([type isEqualToString:@"announce"]) {
1536  NSString* message = annotatedEvent[@"data"][@"message"];
1537  NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1538  if (message == nil) {
1539  return;
1540  }
1541 
1542  NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1543  ? NSAccessibilityPriorityHigh
1544  : NSAccessibilityPriorityMedium;
1545 
1546  [self announceAccessibilityMessage:message withPriority:priority];
1547  }
1548 }
1549 
1550 - (void)announceAccessibilityMessage:(NSString*)message
1551  withPriority:(NSAccessibilityPriorityLevel)priority {
1552  NSAccessibilityPostNotificationWithUserInfo(
1553  [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1554  NSAccessibilityAnnouncementRequestedNotification,
1555  @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1556 }
1557 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
1558  if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
1559  [[NSApplication sharedApplication] terminate:self];
1560  result(nil);
1561  } else if ([call.method isEqualToString:@"SystemSound.play"]) {
1562  [self playSystemSound:call.arguments];
1563  result(nil);
1564  } else if ([call.method isEqualToString:@"Clipboard.getData"]) {
1565  result([self getClipboardData:call.arguments]);
1566  } else if ([call.method isEqualToString:@"Clipboard.setData"]) {
1567  [self setClipboardData:call.arguments];
1568  result(nil);
1569  } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) {
1570  result(@{@"value" : @([self clipboardHasStrings])});
1571  } else if ([call.method isEqualToString:@"System.exitApplication"]) {
1572  if ([self terminationHandler] == nil) {
1573  // If the termination handler isn't set, then either we haven't
1574  // initialized it yet, or (more likely) the NSApp delegate isn't a
1575  // FlutterAppDelegate, so it can't cancel requests to exit. So, in that
1576  // case, just terminate when requested.
1577  [NSApp terminate:self];
1578  result(nil);
1579  } else {
1580  [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1581  }
1582  } else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1583  if ([self terminationHandler] != nil) {
1584  [self terminationHandler].acceptingRequests = YES;
1585  }
1586  result(nil);
1587  } else {
1589  }
1590 }
1591 
1592 - (void)playSystemSound:(NSString*)soundType {
1593  if ([soundType isEqualToString:@"SystemSoundType.alert"]) {
1594  NSBeep();
1595  }
1596 }
1597 
1598 - (NSDictionary*)getClipboardData:(NSString*)format {
1599  if ([format isEqualToString:@(kTextPlainFormat)]) {
1600  NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
1601  return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
1602  }
1603  return nil;
1604 }
1605 
1606 - (void)setClipboardData:(NSDictionary*)data {
1607  NSString* text = data[@"text"];
1608  [self.pasteboard clearContents];
1609  if (text && ![text isEqual:[NSNull null]]) {
1610  [self.pasteboard setString:text forType:NSPasteboardTypeString];
1611  }
1612 }
1613 
1614 - (BOOL)clipboardHasStrings {
1615  return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1616 }
1617 
1618 - (std::vector<std::string>)switches {
1620 }
1621 
1622 #pragma mark - FlutterAppLifecycleDelegate
1623 
1624 - (void)setApplicationState:(flutter::AppLifecycleState)state {
1625  NSString* nextState =
1626  [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1627  [self sendOnChannel:kFlutterLifecycleChannel
1628  message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1629 }
1630 
1631 /**
1632  * Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
1633  * notification.
1634  */
1635 - (void)handleWillBecomeActive:(NSNotification*)notification {
1636  _active = YES;
1637  if (!_visible) {
1638  [self setApplicationState:flutter::AppLifecycleState::kHidden];
1639  } else {
1640  [self setApplicationState:flutter::AppLifecycleState::kResumed];
1641  }
1642 }
1643 
1644 /**
1645  * Called when the |FlutterAppDelegate| gets the applicationWillResignActive
1646  * notification.
1647  */
1648 - (void)handleWillResignActive:(NSNotification*)notification {
1649  _active = NO;
1650  if (!_visible) {
1651  [self setApplicationState:flutter::AppLifecycleState::kHidden];
1652  } else {
1653  [self setApplicationState:flutter::AppLifecycleState::kInactive];
1654  }
1655 }
1656 
1657 /**
1658  * Called when the |FlutterAppDelegate| gets the applicationDidUnhide
1659  * notification.
1660  */
1661 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1662  NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1663  if (occlusionState & NSApplicationOcclusionStateVisible) {
1664  _visible = YES;
1665  if (_active) {
1666  [self setApplicationState:flutter::AppLifecycleState::kResumed];
1667  } else {
1668  [self setApplicationState:flutter::AppLifecycleState::kInactive];
1669  }
1670  } else {
1671  _visible = NO;
1672  [self setApplicationState:flutter::AppLifecycleState::kHidden];
1673  }
1674 }
1675 
1676 #pragma mark - FlutterBinaryMessenger
1677 
1678 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1679  [self sendOnChannel:channel message:message binaryReply:nil];
1680 }
1681 
1682 - (void)sendOnChannel:(NSString*)channel
1683  message:(NSData* _Nullable)message
1684  binaryReply:(FlutterBinaryReply _Nullable)callback {
1685  FlutterPlatformMessageResponseHandle* response_handle = nullptr;
1686  if (callback) {
1687  struct Captures {
1688  FlutterBinaryReply reply;
1689  };
1690  auto captures = std::make_unique<Captures>();
1691  captures->reply = callback;
1692  auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) {
1693  auto captures = reinterpret_cast<Captures*>(user_data);
1694  NSData* reply_data = nil;
1695  if (data != nullptr && data_size > 0) {
1696  reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1697  }
1698  captures->reply(reply_data);
1699  delete captures;
1700  };
1701 
1702  FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1703  _engine, message_reply, captures.get(), &response_handle);
1704  if (create_result != kSuccess) {
1705  NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1706  return;
1707  }
1708  captures.release();
1709  }
1710 
1711  FlutterPlatformMessage platformMessage = {
1712  .struct_size = sizeof(FlutterPlatformMessage),
1713  .channel = [channel UTF8String],
1714  .message = static_cast<const uint8_t*>(message.bytes),
1715  .message_size = message.length,
1716  .response_handle = response_handle,
1717  };
1718 
1719  FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1720  if (message_result != kSuccess) {
1721  NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1722  message_result);
1723  }
1724 
1725  if (response_handle != nullptr) {
1726  FlutterEngineResult release_result =
1727  _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1728  if (release_result != kSuccess) {
1729  NSLog(@"Failed to release the response handle (%d).", release_result);
1730  };
1731  }
1732 }
1733 
1734 - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel
1735  binaryMessageHandler:
1736  (nullable FlutterBinaryMessageHandler)handler {
1738  _messengerHandlers[channel] =
1739  [[FlutterEngineHandlerInfo alloc] initWithConnection:@(_currentMessengerConnection)
1740  handler:[handler copy]];
1742 }
1743 
1744 - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1745  // Find the _messengerHandlers that has the required connection, and record its
1746  // channel.
1747  NSString* foundChannel = nil;
1748  for (NSString* key in [_messengerHandlers allKeys]) {
1749  FlutterEngineHandlerInfo* handlerInfo = [_messengerHandlers objectForKey:key];
1750  if ([handlerInfo.connection isEqual:@(connection)]) {
1751  foundChannel = key;
1752  break;
1753  }
1754  }
1755  if (foundChannel) {
1756  [_messengerHandlers removeObjectForKey:foundChannel];
1757  }
1758 }
1759 
1760 #pragma mark - FlutterPluginRegistry
1761 
1762 - (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1763  id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
1764  if (!registrar) {
1765  FlutterEngineRegistrar* registrarImpl =
1766  [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
1767  self.pluginRegistrars[pluginName] = registrarImpl;
1768  registrar = registrarImpl;
1769  }
1770  return registrar;
1771 }
1772 
1773 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1774  return self.pluginRegistrars[pluginName].publishedValue;
1775 }
1776 
1777 #pragma mark - FlutterTextureRegistrar
1778 
1779 - (int64_t)registerTexture:(id<FlutterTexture>)texture {
1780  return [_renderer registerTexture:texture];
1781 }
1782 
1783 - (BOOL)registerTextureWithID:(int64_t)textureId {
1784  return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1785 }
1786 
1787 - (void)textureFrameAvailable:(int64_t)textureID {
1788  [_renderer textureFrameAvailable:textureID];
1789 }
1790 
1791 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1792  return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1793 }
1794 
1795 - (void)unregisterTexture:(int64_t)textureID {
1796  [_renderer unregisterTexture:textureID];
1797 }
1798 
1799 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1800  return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1801 }
1802 
1803 #pragma mark - Task runner integration
1804 
1805 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1806  __weak FlutterEngine* weakSelf = self;
1807 
1808  const auto engine_time = _embedderAPI.GetCurrentTime();
1809  [FlutterRunLoop.mainRunLoop
1810  performAfterDelay:(targetTime - (double)engine_time) / NSEC_PER_SEC
1811  block:^{
1812  FlutterEngine* self = weakSelf;
1813  if (self != nil && self->_engine != nil) {
1814  auto result = _embedderAPI.RunTask(self->_engine, &task);
1815  if (result != kSuccess) {
1816  NSLog(@"Could not post a task to the Flutter engine.");
1817  }
1818  }
1819  }];
1820 }
1821 
1822 // Getter used by test harness, only exposed through the FlutterEngine(Test) category
1823 - (flutter::FlutterCompositor*)macOSCompositor {
1824  return _macOSCompositor.get();
1825 }
1826 
1827 #pragma mark - FlutterKeyboardManagerDelegate
1828 
1829 /**
1830  * Dispatches the given pointer event data to engine.
1831  */
1832 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
1833  callback:(FlutterKeyEventCallback)callback
1834  userData:(void*)userData {
1835  _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
1836 }
1837 
1838 @end
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
int64_t FlutterBinaryMessengerConnection
void(^ FlutterResult)(id _Nullable result)
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
FlutterBinaryMessengerConnection _connection
FlutterMethodChannel * _platformViewsChannel
_FlutterEngineAOTData * _aotData
std::unique_ptr< flutter::FlutterCompositor > _macOSCompositor
static const int kMainThreadPriority
static void OnPlatformMessage(const FlutterPlatformMessage *message, void *user_data)
FlutterPlatformViewController * _platformViewController
FlutterBasicMessageChannel * _accessibilityChannel
FlutterBasicMessageChannel * _settingsChannel
static FlutterLocale FlutterLocaleFromNSLocale(NSLocale *locale)
BOOL _allowHeadlessExecution
NSMutableDictionary< NSString *, FlutterEngineHandlerInfo * > * _messengerHandlers
FlutterBinaryMessengerConnection _currentMessengerConnection
FlutterMethodChannel * _platformChannel
NSString *const kFlutterLifecycleChannel
static NSString *const kEnhancedUserInterfaceNotification
The private notification for voice over.
NSMapTable< NSNumber *, FlutterVSyncWaiter * > * _vsyncWaiters
FlutterViewIdentifier _nextViewIdentifier
NSString *const kFlutterPlatformChannel
FlutterMethodChannel * _screenshotChannel
FlutterTextInputPlugin * _textInputPlugin
FlutterDartProject * _project
NSMapTable * _viewControllers
BOOL _multiViewEnabled
FlutterCompositor _compositor
__weak FlutterView * _lastViewWithPointerEvent
FlutterKeyboardManager * _keyboardManager
FlutterWindowController * _windowController
FlutterTerminationCallback _terminator
constexpr char kTextPlainFormat[]
Clipboard plain text format.
__weak FlutterEngine * _flutterEngine
BOOL _visible
BOOL _active
FlutterBinaryMessengerRelay * _binaryMessenger
static NSString *const kEnhancedUserInterfaceKey
NSString *const kFlutterSettingsChannel
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)
int64_t FlutterViewIdentifier
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
NSString * lookupKeyForAsset:(NSString *asset)
NSInteger clearContents()
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
FlutterBinaryMessageHandler handler
id< FlutterBinaryMessenger > binaryMessenger
Definition: FlutterEngine.h:92
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
void registerWithRegistrar:(nonnull id< FlutterPluginRegistrar > registrar)
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
void setMethodCallHandler:(FlutterMethodCallHandler _Nullable handler)
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
void registerWithRegistrar:delegate:(nonnull id< FlutterPluginRegistrar > registrar,[delegate] nullable id< FlutterMouseCursorPluginDelegate > delegate)
instancetype typedDataWithBytes:(NSData *data)
Converts between the time representation used by Flutter Engine and CAMediaTime.
uint64_t CAMediaTimeToEngineTime:(CFTimeInterval time)
void waitForVSync:(uintptr_t baton)
FlutterViewIdentifier viewIdentifier
void onAccessibilityStatusChanged:(BOOL enabled)
BOOL sizedToContents
Definition: FlutterView.h:121
std::vector< std::string > GetSwitchesFromEnvironment()
instancetype sharedInstance()
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)
void * user_data