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