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