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