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