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