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