Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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 */
92@interface FlutterEngine () <FlutterBinaryMessenger, FlutterMouseCursorPluginDelegate>
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
405
406#pragma mark -
407
408@implementation FlutterEngine {
409 // The embedding-API-level engine object.
411
412 // The project being run by this engine.
414
415 // A mapping of channel names to the registered information for those channels.
416 NSMutableDictionary<NSString*, FlutterEngineHandlerInfo*>* _messengerHandlers;
417
418 // A self-incremental integer to assign to newly assigned channels as
419 // identification.
421
422 // Whether the engine can continue running after the view controller is removed.
424
425 // Pointer to the Dart AOT snapshot and instruction data.
427
428 // _macOSCompositor is created when the engine is created and its destruction is handled by ARC
429 // when the engine is destroyed.
430 std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
431
432 // The information of all views attached to this engine mapped from IDs.
433 //
434 // It can't use NSDictionary, because the values need to be weak references.
435 NSMapTable* _viewControllers;
436
437 // FlutterCompositor is copied and used in embedder.cc.
439
440 // Method channel for platform view functions. These functions include creating, disposing and
441 // mutating a platform view.
443
444 // Used to support creation and deletion of platform views and registering platform view
445 // factories. Lifecycle is tied to the engine.
447
448 // A message channel for sending user settings to the flutter engine.
450
451 // A message channel for accessibility.
453
454 // A method channel for miscellaneous platform functionality.
456
458
459 // Whether the application is currently the active application.
461
462 // Whether any portion of the application is currently visible.
464
465 // Proxy to allow plugins, channels to hold a weak reference to the binary messenger (self).
467
468 // Map from ViewId to vsync waiter. Note that this is modified on main thread
469 // but accessed on UI thread, so access must be @synchronized.
470 NSMapTable<NSNumber*, FlutterVSyncWaiter*>* _vsyncWaiters;
471
472 // Weak reference to last view that received a pointer event. This is used to
473 // pair cursor change with a view.
475}
476
477- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
478 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
479}
480
481static const int kMainThreadPriority = 47;
482
483static void SetThreadPriority(FlutterThreadPriority priority) {
484 if (priority == kDisplay || priority == kRaster) {
485 pthread_t thread = pthread_self();
486 sched_param param;
487 int policy;
488 if (!pthread_getschedparam(thread, &policy, &param)) {
489 param.sched_priority = kMainThreadPriority;
490 pthread_setschedparam(thread, policy, &param);
491 }
492 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
493 }
494}
495
496- (instancetype)initWithName:(NSString*)labelPrefix
497 project:(FlutterDartProject*)project
498 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
499 self = [super init];
500 NSAssert(self, @"Super init cannot be nil");
501 _pasteboard = [[FlutterPasteboard alloc] init];
502 _active = NO;
503 _visible = NO;
504 _project = project ?: [[FlutterDartProject alloc] init];
505 _messengerHandlers = [[NSMutableDictionary alloc] init];
506 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
507 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
508 _pluginRegistrars = [[NSMutableDictionary alloc] init];
510 _allowHeadlessExecution = allowHeadlessExecution;
511 _semanticsEnabled = NO;
512 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
513 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
514 [_isResponseValid addObject:@YES];
515
516 _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
517 FlutterEngineGetProcAddresses(&_embedderAPI);
518
519 _viewControllers = [NSMapTable weakToWeakObjectsMapTable];
520 _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self];
521
522 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
523 [notificationCenter addObserver:self
524 selector:@selector(sendUserLocales)
525 name:NSCurrentLocaleDidChangeNotification
526 object:nil];
527
530 [self setUpPlatformViewChannel];
531 [self setUpAccessibilityChannel];
532 [self setUpNotificationCenterListeners];
533 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
534 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
535 _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self
536 terminator:nil];
537 id<FlutterAppLifecycleProvider> lifecycleProvider =
538 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
539 [lifecycleProvider addApplicationLifecycleDelegate:self];
540 } else {
541 _terminationHandler = nil;
542 }
543
544 _vsyncWaiters = [NSMapTable strongToStrongObjectsMapTable];
545
546 return self;
547}
548
549- (void)dealloc {
550 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
551 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
552 id<FlutterAppLifecycleProvider> lifecycleProvider =
553 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
554 [lifecycleProvider removeApplicationLifecycleDelegate:self];
555
556 // Unregister any plugins that registered as app delegates, since they are not guaranteed to
557 // live after the engine is destroyed, and their delegation registration is intended to be bound
558 // to the engine and its lifetime.
559 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
560 if (delegate) {
561 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
562 }
563 }
564 }
565 // Clear any published values, just in case a plugin has created a retain cycle with the
566 // registrar.
567 for (NSString* pluginName in _pluginRegistrars) {
568 [_pluginRegistrars[pluginName] publish:[NSNull null]];
569 }
570 @synchronized(_isResponseValid) {
571 [_isResponseValid removeAllObjects];
572 [_isResponseValid addObject:@NO];
573 }
574 [self shutDownEngine];
575 if (_aotData) {
576 _embedderAPI.CollectAOTData(_aotData);
577 }
578}
579
580- (BOOL)runWithEntrypoint:(NSString*)entrypoint {
581 if (self.running) {
582 return NO;
583 }
584
586 NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
587 return NO;
588 }
589
590 [self addInternalPlugins];
591
592 // The first argument of argv is required to be the executable name.
593 std::vector<const char*> argv = {[self.executableName UTF8String]};
594 std::vector<std::string> switches = self.switches;
595
596 // Enable Impeller only if specifically asked for from the project or cmdline arguments.
598 std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end()) {
599 switches.push_back("--enable-impeller=true");
600 }
601
602 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
603 [](const std::string& arg) -> const char* { return arg.c_str(); });
604
605 std::vector<const char*> dartEntrypointArgs;
606 for (NSString* argument in [_project dartEntrypointArguments]) {
607 dartEntrypointArgs.push_back([argument UTF8String]);
608 }
609
610 FlutterProjectArgs flutterArguments = {};
611 flutterArguments.struct_size = sizeof(FlutterProjectArgs);
612 flutterArguments.assets_path = _project.assetsPath.UTF8String;
613 flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String;
614 flutterArguments.command_line_argc = static_cast<int>(argv.size());
615 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
617 flutterArguments.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
618 void* user_data) {
619 // TODO(dkwingsmt): This callback only supports single-view, therefore it
620 // only operates on the implicit view. To support multi-view, we need a
621 // way to pass in the ID (probably through FlutterSemanticsUpdate).
623 [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
624 };
625 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
626 flutterArguments.shutdown_dart_vm_when_done = true;
627 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
628 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
630 flutterArguments.log_message_callback = [](const char* tag, const char* message,
631 void* user_data) {
632 if (tag && tag[0]) {
633 std::cout << tag << ": ";
634 }
635 std::cout << message << std::endl;
636 };
637
638 static size_t sTaskRunnerIdentifiers = 0;
639 const FlutterTaskRunnerDescription cocoa_task_runner_description = {
641 .user_data = (void*)CFBridgingRetain(self),
642 .runs_task_on_current_thread_callback = [](void* user_data) -> bool {
643 return [[NSThread currentThread] isMainThread];
644 },
645 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
646 void* user_data) -> void {
647 [((__bridge FlutterEngine*)(user_data)) postMainThreadTask:task
648 targetTimeInNanoseconds:target_time_nanos];
649 },
650 .identifier = ++sTaskRunnerIdentifiers,
651 };
652 const FlutterCustomTaskRunners custom_task_runners = {
654 .platform_task_runner = &cocoa_task_runner_description,
655 .thread_priority_setter = SetThreadPriority};
656 flutterArguments.custom_task_runners = &custom_task_runners;
657
658 [self loadAOTData:_project.assetsPath];
659 if (_aotData) {
660 flutterArguments.aot_data = _aotData;
661 }
662
663 flutterArguments.compositor = [self createFlutterCompositor];
664
665 flutterArguments.on_pre_engine_restart_callback = [](void* user_data) {
668 };
669
670 flutterArguments.vsync_callback = [](void* user_data, intptr_t baton) {
672 [engine onVSync:baton];
673 };
674
675 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
676 FlutterEngineResult result = _embedderAPI.Initialize(
677 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
678 if (result != kSuccess) {
679 NSLog(@"Failed to initialize Flutter engine: error %d", result);
680 return NO;
681 }
682
683 result = _embedderAPI.RunInitialized(_engine);
684 if (result != kSuccess) {
685 NSLog(@"Failed to run an initialized engine: error %d", result);
686 return NO;
687 }
688
689 [self sendUserLocales];
690
691 // Update window metric for all view controllers.
692 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
693 FlutterViewController* nextViewController;
694 while ((nextViewController = [viewControllerEnumerator nextObject])) {
695 [self updateWindowMetricsForViewController:nextViewController];
696 }
697
698 [self updateDisplayConfig];
699 // Send the initial user settings such as brightness and text scale factor
700 // to the engine.
701 [self sendInitialSettings];
702 return YES;
703}
704
705- (void)loadAOTData:(NSString*)assetsDir {
706 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
707 return;
708 }
709
710 BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath.
711 NSFileManager* fileManager = [NSFileManager defaultManager];
712
713 // This is the location where the test fixture places the snapshot file.
714 // For applications built by Flutter tool, this is in "App.framework".
715 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
716
717 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
718 return;
719 }
720
723 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
724
725 auto result = _embedderAPI.CreateAOTData(&source, &_aotData);
726 if (result != kSuccess) {
727 NSLog(@"Failed to load AOT data from: %@", elfPath);
728 }
729}
730
731- (void)registerViewController:(FlutterViewController*)controller
732 forIdentifier:(FlutterViewIdentifier)viewIdentifier {
733 NSAssert(controller != nil, @"The controller must not be nil.");
734 NSAssert(controller.engine == nil,
735 @"The FlutterViewController is unexpectedly attached to "
736 @"engine %@ before initialization.",
737 controller.engine);
738 NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil,
739 @"The requested view ID is occupied.");
740 [_viewControllers setObject:controller forKey:@(viewIdentifier)];
741 [controller setUpWithEngine:self
742 viewIdentifier:viewIdentifier
743 threadSynchronizer:_threadSynchronizer];
744 NSAssert(controller.viewIdentifier == viewIdentifier, @"Failed to assign view ID.");
745 // Verify that the controller's property are updated accordingly. Failing the
746 // assertions is likely because either the FlutterViewController or the
747 // FlutterEngine is mocked. Please subclass these classes instead.
748 NSAssert(controller.attached, @"The FlutterViewController should switch to the attached mode "
749 @"after it is added to a FlutterEngine.");
750 NSAssert(controller.engine == self,
751 @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
752 self, controller.engine);
753
754 if (controller.viewLoaded) {
755 [self viewControllerViewDidLoad:controller];
756 }
757}
758
759- (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
760 __weak FlutterEngine* weakSelf = self;
761 FlutterTimeConverter* timeConverter = [[FlutterTimeConverter alloc] initWithEngine:self];
762 FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
763 initWithDisplayLink:[FlutterDisplayLink displayLinkWithView:viewController.view]
764 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
765 uintptr_t baton) {
766 uint64_t timeNanos = [timeConverter CAMediaTimeToEngineTime:timestamp];
767 uint64_t targetTimeNanos =
768 [timeConverter CAMediaTimeToEngineTime:targetTimestamp];
769 FlutterEngine* engine = weakSelf;
770 if (engine) {
771 // It is a bit unfortunate that embedder requires OnVSync call on
772 // platform thread just to immediately redispatch it to UI thread.
773 // We are already on UI thread right now, but have to do the
774 // extra hop to main thread.
775 [engine->_threadSynchronizer performOnPlatformThread:^{
776 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
777 }];
778 }
779 }];
780 FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil);
781 @synchronized(_vsyncWaiters) {
782 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
783 }
784}
785
786- (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
787 FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier];
788 // The controller can be nil. The engine stores only a weak ref, and this
789 // method could have been called from the controller's dealloc.
790 if (controller != nil) {
791 [controller detachFromEngine];
792 NSAssert(!controller.attached,
793 @"The FlutterViewController unexpectedly stays attached after being removed. "
794 @"In unit tests, this is likely because either the FlutterViewController or "
795 @"the FlutterEngine is mocked. Please subclass these classes instead.");
796 }
797 [_viewControllers removeObjectForKey:@(viewIdentifier)];
798 @synchronized(_vsyncWaiters) {
799 [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
800 }
801}
802
803- (void)shutDownIfNeeded {
805 [self shutDownEngine];
806 }
807}
808
809- (FlutterViewController*)viewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
810 FlutterViewController* controller = [_viewControllers objectForKey:@(viewIdentifier)];
811 NSAssert(controller == nil || controller.viewIdentifier == viewIdentifier,
812 @"The stored controller has unexpected view ID.");
813 return controller;
814}
815
816- (void)setViewController:(FlutterViewController*)controller {
817 FlutterViewController* currentController =
818 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
819 if (currentController == controller) {
820 // From nil to nil, or from non-nil to the same controller.
821 return;
822 }
823 if (currentController == nil && controller != nil) {
824 // From nil to non-nil.
825 NSAssert(controller.engine == nil,
826 @"Failed to set view controller to the engine: "
827 @"The given FlutterViewController is already attached to an engine %@. "
828 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
829 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
830 controller.engine);
831 [self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
832 } else if (currentController != nil && controller == nil) {
833 NSAssert(currentController.viewIdentifier == kFlutterImplicitViewId,
834 @"The default controller has an unexpected ID %llu", currentController.viewIdentifier);
835 // From non-nil to nil.
836 [self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
837 [self shutDownIfNeeded];
838 } else {
839 // From non-nil to a different non-nil view controller.
840 NSAssert(NO,
841 @"Failed to set view controller to the engine: "
842 @"The engine already has an implicit view controller %@. "
843 @"If you wanted to make the implicit view render in a different window, "
844 @"you should attach the current view controller to the window instead.",
845 [_viewControllers objectForKey:@(kFlutterImplicitViewId)]);
846 }
847}
848
850 return [self viewControllerForIdentifier:kFlutterImplicitViewId];
851}
852
853- (FlutterCompositor*)createFlutterCompositor {
854 _macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
855 [[FlutterViewEngineProvider alloc] initWithEngine:self],
856 [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController);
857
858 _compositor = {};
861
863 FlutterBackingStore* backing_store_out, //
864 void* user_data //
865 ) {
866 return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
867 config, backing_store_out);
868 };
869
871 void* user_data //
872 ) { return true; };
873
875 return reinterpret_cast<flutter::FlutterCompositor*>(info->user_data)
876 ->Present(info->view_id, info->layers, info->layers_count);
877 };
878
880
881 return &_compositor;
882}
883
884- (id<FlutterBinaryMessenger>)binaryMessenger {
885 return _binaryMessenger;
886}
887
888#pragma mark - Framework-internal methods
889
890- (void)addViewController:(FlutterViewController*)controller {
891 // FlutterEngine can only handle the implicit view for now. Adding more views
892 // throws an assertion.
893 NSAssert(self.viewController == nil,
894 @"The engine already has a view controller for the implicit view.");
895 self.viewController = controller;
896}
897
898- (void)removeViewController:(nonnull FlutterViewController*)viewController {
899 [self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
900 [self shutDownIfNeeded];
901}
902
903- (BOOL)running {
904 return _engine != nullptr;
905}
906
907- (void)updateDisplayConfig:(NSNotification*)notification {
908 [self updateDisplayConfig];
909}
910
911- (NSArray<NSScreen*>*)screens {
912 return [NSScreen screens];
913}
914
915- (void)updateDisplayConfig {
916 if (!_engine) {
917 return;
918 }
919
920 std::vector<FlutterEngineDisplay> displays;
921 for (NSScreen* screen : [self screens]) {
922 CGDirectDisplayID displayID =
923 static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
924
925 double devicePixelRatio = screen.backingScaleFactor;
926 FlutterEngineDisplay display;
927 display.struct_size = sizeof(display);
928 display.display_id = displayID;
929 display.single_display = false;
930 display.width = static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
931 display.height = static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
932 display.device_pixel_ratio = devicePixelRatio;
933
934 CVDisplayLinkRef displayLinkRef = nil;
935 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
936
937 if (error == 0) {
938 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
939 if (!(nominal.flags & kCVTimeIsIndefinite)) {
940 double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
941 display.refresh_rate = round(refreshRate);
942 }
943 CVDisplayLinkRelease(displayLinkRef);
944 } else {
945 display.refresh_rate = 0;
946 }
947
948 displays.push_back(display);
949 }
950 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
951 displays.data(), displays.size());
952}
953
954- (void)onSettingsChanged:(NSNotification*)notification {
955 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
956 NSString* brightness =
957 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
958 [_settingsChannel sendMessage:@{
959 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
960 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
961 @"textScaleFactor" : @1.0,
962 @"alwaysUse24HourFormat" : @false
963 }];
964}
965
966- (void)sendInitialSettings {
967 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
968 [[NSDistributedNotificationCenter defaultCenter]
969 addObserver:self
970 selector:@selector(onSettingsChanged:)
971 name:@"AppleInterfaceThemeChangedNotification"
972 object:nil];
973 [self onSettingsChanged:nil];
974}
975
976- (FlutterEngineProcTable&)embedderAPI {
977 return _embedderAPI;
978}
979
980- (nonnull NSString*)executableName {
981 return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter";
982}
983
984- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
985 if (!_engine || !viewController || !viewController.viewLoaded) {
986 return;
987 }
988 NSAssert([self viewControllerForIdentifier:viewController.viewIdentifier] == viewController,
989 @"The provided view controller is not attached to this engine.");
990 NSView* view = viewController.flutterView;
991 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
992 CGSize scaledSize = scaledBounds.size;
993 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
994 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
995 const FlutterWindowMetricsEvent windowMetricsEvent = {
996 .struct_size = sizeof(windowMetricsEvent),
997 .width = static_cast<size_t>(scaledSize.width),
998 .height = static_cast<size_t>(scaledSize.height),
999 .pixel_ratio = pixelRatio,
1000 .left = static_cast<size_t>(scaledBounds.origin.x),
1001 .top = static_cast<size_t>(scaledBounds.origin.y),
1002 .display_id = static_cast<uint64_t>(displayId),
1003 .view_id = viewController.viewIdentifier,
1004 };
1005 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1006}
1007
1008- (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1009 _embedderAPI.SendPointerEvent(_engine, &event, 1);
1010 _lastViewWithPointerEvent = [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView;
1011}
1012
1013- (void)sendKeyEvent:(const FlutterKeyEvent&)event
1014 callback:(FlutterKeyEventCallback)callback
1015 userData:(void*)userData {
1016 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
1017}
1018
1019- (void)setSemanticsEnabled:(BOOL)enabled {
1020 if (_semanticsEnabled == enabled) {
1021 return;
1022 }
1023 _semanticsEnabled = enabled;
1024
1025 // Update all view controllers' bridges.
1026 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1027 FlutterViewController* nextViewController;
1028 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1029 [nextViewController notifySemanticsEnabledChanged];
1030 }
1031
1032 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1033}
1034
1035- (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1036 toTarget:(uint16_t)target
1037 withData:(fml::MallocMapping)data {
1038 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1039}
1040
1041- (FlutterPlatformViewController*)platformViewController {
1043}
1044
1045#pragma mark - Private methods
1046
1047- (void)sendUserLocales {
1048 if (!self.running) {
1049 return;
1050 }
1051
1052 // Create a list of FlutterLocales corresponding to the preferred languages.
1053 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1054 std::vector<FlutterLocale> flutterLocales;
1055 flutterLocales.reserve(locales.count);
1056 for (NSString* localeID in [NSLocale preferredLanguages]) {
1057 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1058 [locales addObject:locale];
1059 flutterLocales.push_back(FlutterLocaleFromNSLocale(locale));
1060 }
1061 // Convert to a list of pointers, and send to the engine.
1062 std::vector<const FlutterLocale*> flutterLocaleList;
1063 flutterLocaleList.reserve(flutterLocales.size());
1064 std::transform(flutterLocales.begin(), flutterLocales.end(),
1065 std::back_inserter(flutterLocaleList),
1066 [](const auto& arg) -> const auto* { return &arg; });
1067 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1068}
1069
1070- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1071 NSData* messageData = nil;
1072 if (message->message_size > 0) {
1073 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1074 length:message->message_size
1075 freeWhenDone:NO];
1076 }
1077 NSString* channel = @(message->channel);
1078 __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1079 __block FlutterEngine* weakSelf = self;
1080 NSMutableArray* isResponseValid = self.isResponseValid;
1081 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1082 _embedderAPI.SendPlatformMessageResponse;
1083 FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
1084 @synchronized(isResponseValid) {
1085 if (![isResponseValid[0] boolValue]) {
1086 // Ignore, engine was killed.
1087 return;
1088 }
1089 if (responseHandle) {
1090 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1091 static_cast<const uint8_t*>(response.bytes), response.length);
1092 responseHandle = NULL;
1093 } else {
1094 NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
1095 "on channel '%@'.",
1096 channel);
1097 }
1098 }
1099 };
1100
1101 FlutterEngineHandlerInfo* handlerInfo = _messengerHandlers[channel];
1102 if (handlerInfo) {
1103 handlerInfo.handler(messageData, binaryResponseHandler);
1104 } else {
1105 binaryResponseHandler(nil);
1106 }
1107}
1108
1109- (void)engineCallbackOnPreEngineRestart {
1110 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1111 FlutterViewController* nextViewController;
1112 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1113 [nextViewController onPreEngineRestart];
1114 }
1115}
1116
1117- (void)onVSync:(uintptr_t)baton {
1118 @synchronized(_vsyncWaiters) {
1119 // TODO(knopp): Use vsync waiter for correct view.
1120 // https://github.com/flutter/flutter/issues/142845
1121 FlutterVSyncWaiter* waiter = [_vsyncWaiters objectForKey:@(kFlutterImplicitViewId)];
1122 [waiter waitForVSync:baton];
1123 }
1124}
1125
1126/**
1127 * Note: Called from dealloc. Should not use accessors or other methods.
1128 */
1129- (void)shutDownEngine {
1130 if (_engine == nullptr) {
1131 return;
1132 }
1133
1134 [_threadSynchronizer shutdown];
1135 _threadSynchronizer = nil;
1136
1137 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1138 if (result != kSuccess) {
1139 NSLog(@"Could not de-initialize the Flutter engine: error %d", result);
1140 }
1141
1142 // Balancing release for the retain in the task runner dispatch table.
1143 CFRelease((CFTypeRef)self);
1144
1145 result = _embedderAPI.Shutdown(_engine);
1146 if (result != kSuccess) {
1147 NSLog(@"Failed to shut down Flutter engine: error %d", result);
1148 }
1149 _engine = nullptr;
1150}
1151
1152- (void)setUpPlatformViewChannel {
1154 [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
1155 binaryMessenger:self.binaryMessenger
1157
1158 __weak FlutterEngine* weakSelf = self;
1159 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1160 [[weakSelf platformViewController] handleMethodCall:call result:result];
1161 }];
1162}
1163
1164- (void)setUpAccessibilityChannel {
1166 messageChannelWithName:@"flutter/accessibility"
1167 binaryMessenger:self.binaryMessenger
1169 __weak FlutterEngine* weakSelf = self;
1170 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1171 [weakSelf handleAccessibilityEvent:message];
1172 }];
1173}
1174- (void)setUpNotificationCenterListeners {
1175 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1176 // macOS fires this private message when VoiceOver turns on or off.
1177 [center addObserver:self
1178 selector:@selector(onAccessibilityStatusChanged:)
1179 name:kEnhancedUserInterfaceNotification
1180 object:nil];
1181 [center addObserver:self
1182 selector:@selector(applicationWillTerminate:)
1183 name:NSApplicationWillTerminateNotification
1184 object:nil];
1185 [center addObserver:self
1186 selector:@selector(windowDidChangeScreen:)
1187 name:NSWindowDidChangeScreenNotification
1188 object:nil];
1189 [center addObserver:self
1190 selector:@selector(updateDisplayConfig:)
1191 name:NSApplicationDidChangeScreenParametersNotification
1192 object:nil];
1193}
1194
1195- (void)addInternalPlugins {
1196 __weak FlutterEngine* weakSelf = self;
1197 [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]
1198 delegate:self];
1199 [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
1201 [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel
1202 binaryMessenger:self.binaryMessenger
1205 [FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel
1206 binaryMessenger:self.binaryMessenger
1208 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1209 [weakSelf handleMethodCall:call result:result];
1210 }];
1211}
1212
1213- (void)didUpdateMouseCursor:(NSCursor*)cursor {
1214 // Mouse cursor plugin does not specify which view is responsible for changing the cursor,
1215 // so the reasonable assumption here is that cursor change is a result of a mouse movement
1216 // and thus the cursor will be paired with last Flutter view that reveived mouse event.
1217 [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1218}
1219
1220- (void)applicationWillTerminate:(NSNotification*)notification {
1221 [self shutDownEngine];
1222}
1223
1224- (void)windowDidChangeScreen:(NSNotification*)notification {
1225 // Update window metric for all view controllers since the display_id has
1226 // changed.
1227 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1228 FlutterViewController* nextViewController;
1229 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1230 [self updateWindowMetricsForViewController:nextViewController];
1231 }
1232}
1233
1234- (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1235 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1236 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1237 FlutterViewController* nextViewController;
1238 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1239 [nextViewController onAccessibilityStatusChanged:enabled];
1240 }
1241
1242 self.semanticsEnabled = enabled;
1243}
1244- (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
1245 NSString* type = annotatedEvent[@"type"];
1246 if ([type isEqualToString:@"announce"]) {
1247 NSString* message = annotatedEvent[@"data"][@"message"];
1248 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1249 if (message == nil) {
1250 return;
1251 }
1252
1253 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1254 ? NSAccessibilityPriorityHigh
1255 : NSAccessibilityPriorityMedium;
1256
1257 [self announceAccessibilityMessage:message withPriority:priority];
1258 }
1259}
1260
1261- (void)announceAccessibilityMessage:(NSString*)message
1262 withPriority:(NSAccessibilityPriorityLevel)priority {
1263 NSAccessibilityPostNotificationWithUserInfo(
1264 [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1265 NSAccessibilityAnnouncementRequestedNotification,
1266 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1267}
1268- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
1269 if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
1270 [[NSApplication sharedApplication] terminate:self];
1271 result(nil);
1272 } else if ([call.method isEqualToString:@"SystemSound.play"]) {
1273 [self playSystemSound:call.arguments];
1274 result(nil);
1275 } else if ([call.method isEqualToString:@"Clipboard.getData"]) {
1276 result([self getClipboardData:call.arguments]);
1277 } else if ([call.method isEqualToString:@"Clipboard.setData"]) {
1278 [self setClipboardData:call.arguments];
1279 result(nil);
1280 } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) {
1281 result(@{@"value" : @([self clipboardHasStrings])});
1282 } else if ([call.method isEqualToString:@"System.exitApplication"]) {
1283 if ([self terminationHandler] == nil) {
1284 // If the termination handler isn't set, then either we haven't
1285 // initialized it yet, or (more likely) the NSApp delegate isn't a
1286 // FlutterAppDelegate, so it can't cancel requests to exit. So, in that
1287 // case, just terminate when requested.
1288 [NSApp terminate:self];
1289 result(nil);
1290 } else {
1291 [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1292 }
1293 } else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1294 if ([self terminationHandler] != nil) {
1295 [self terminationHandler].acceptingRequests = YES;
1296 }
1297 result(nil);
1298 } else {
1300 }
1301}
1302
1303- (void)playSystemSound:(NSString*)soundType {
1304 if ([soundType isEqualToString:@"SystemSoundType.alert"]) {
1305 NSBeep();
1306 }
1307}
1308
1309- (NSDictionary*)getClipboardData:(NSString*)format {
1310 if ([format isEqualToString:@(kTextPlainFormat)]) {
1311 NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
1312 return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
1313 }
1314 return nil;
1315}
1316
1317- (void)setClipboardData:(NSDictionary*)data {
1318 NSString* text = data[@"text"];
1319 [self.pasteboard clearContents];
1320 if (text && ![text isEqual:[NSNull null]]) {
1321 [self.pasteboard setString:text forType:NSPasteboardTypeString];
1322 }
1323}
1324
1325- (BOOL)clipboardHasStrings {
1326 return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1327}
1328
1329- (std::vector<std::string>)switches {
1331}
1332
1333- (FlutterThreadSynchronizer*)testThreadSynchronizer {
1334 return _threadSynchronizer;
1335}
1336
1337#pragma mark - FlutterAppLifecycleDelegate
1338
1339- (void)setApplicationState:(flutter::AppLifecycleState)state {
1340 NSString* nextState =
1341 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1342 [self sendOnChannel:kFlutterLifecycleChannel
1343 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1344}
1345
1346/**
1347 * Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
1348 * notification.
1349 */
1350- (void)handleWillBecomeActive:(NSNotification*)notification {
1351 _active = YES;
1352 if (!_visible) {
1353 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1354 } else {
1355 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1356 }
1357}
1358
1359/**
1360 * Called when the |FlutterAppDelegate| gets the applicationWillResignActive
1361 * notification.
1362 */
1363- (void)handleWillResignActive:(NSNotification*)notification {
1364 _active = NO;
1365 if (!_visible) {
1366 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1367 } else {
1368 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1369 }
1370}
1371
1372/**
1373 * Called when the |FlutterAppDelegate| gets the applicationDidUnhide
1374 * notification.
1375 */
1376- (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1377 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1378 if (occlusionState & NSApplicationOcclusionStateVisible) {
1379 _visible = YES;
1380 if (_active) {
1381 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1382 } else {
1383 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1384 }
1385 } else {
1386 _visible = NO;
1387 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1388 }
1389}
1390
1391#pragma mark - FlutterBinaryMessenger
1392
1393- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1394 [self sendOnChannel:channel message:message binaryReply:nil];
1395}
1396
1397- (void)sendOnChannel:(NSString*)channel
1398 message:(NSData* _Nullable)message
1399 binaryReply:(FlutterBinaryReply _Nullable)callback {
1400 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
1401 if (callback) {
1402 struct Captures {
1403 FlutterBinaryReply reply;
1404 };
1405 auto captures = std::make_unique<Captures>();
1406 captures->reply = callback;
1407 auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) {
1408 auto captures = reinterpret_cast<Captures*>(user_data);
1409 NSData* reply_data = nil;
1410 if (data != nullptr && data_size > 0) {
1411 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1412 }
1413 captures->reply(reply_data);
1414 delete captures;
1415 };
1416
1417 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1418 _engine, message_reply, captures.get(), &response_handle);
1419 if (create_result != kSuccess) {
1420 NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1421 return;
1422 }
1423 captures.release();
1424 }
1425
1426 FlutterPlatformMessage platformMessage = {
1428 .channel = [channel UTF8String],
1429 .message = static_cast<const uint8_t*>(message.bytes),
1430 .message_size = message.length,
1431 .response_handle = response_handle,
1432 };
1433
1434 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1435 if (message_result != kSuccess) {
1436 NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1437 message_result);
1438 }
1439
1440 if (response_handle != nullptr) {
1441 FlutterEngineResult release_result =
1442 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1443 if (release_result != kSuccess) {
1444 NSLog(@"Failed to release the response handle (%d).", release_result);
1445 };
1446 }
1447}
1448
1449- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel
1450 binaryMessageHandler:
1451 (nullable FlutterBinaryMessageHandler)handler {
1453 _messengerHandlers[channel] =
1454 [[FlutterEngineHandlerInfo alloc] initWithConnection:@(_currentMessengerConnection)
1455 handler:[handler copy]];
1457}
1458
1459- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1460 // Find the _messengerHandlers that has the required connection, and record its
1461 // channel.
1462 NSString* foundChannel = nil;
1463 for (NSString* key in [_messengerHandlers allKeys]) {
1464 FlutterEngineHandlerInfo* handlerInfo = [_messengerHandlers objectForKey:key];
1465 if ([handlerInfo.connection isEqual:@(connection)]) {
1466 foundChannel = key;
1467 break;
1468 }
1469 }
1470 if (foundChannel) {
1471 [_messengerHandlers removeObjectForKey:foundChannel];
1472 }
1473}
1474
1475#pragma mark - FlutterPluginRegistry
1476
1477- (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1478 id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
1479 if (!registrar) {
1480 FlutterEngineRegistrar* registrarImpl =
1481 [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
1482 self.pluginRegistrars[pluginName] = registrarImpl;
1483 registrar = registrarImpl;
1484 }
1485 return registrar;
1486}
1487
1488- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1489 return self.pluginRegistrars[pluginName].publishedValue;
1490}
1491
1492#pragma mark - FlutterTextureRegistrar
1493
1494- (int64_t)registerTexture:(id<FlutterTexture>)texture {
1495 return [_renderer registerTexture:texture];
1496}
1497
1498- (BOOL)registerTextureWithID:(int64_t)textureId {
1499 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1500}
1501
1502- (void)textureFrameAvailable:(int64_t)textureID {
1503 [_renderer textureFrameAvailable:textureID];
1504}
1505
1506- (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1507 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1508}
1509
1510- (void)unregisterTexture:(int64_t)textureID {
1511 [_renderer unregisterTexture:textureID];
1512}
1513
1514- (BOOL)unregisterTextureWithID:(int64_t)textureID {
1515 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1516}
1517
1518#pragma mark - Task runner integration
1519
1520- (void)runTaskOnEmbedder:(FlutterTask)task {
1521 if (_engine) {
1522 auto result = _embedderAPI.RunTask(_engine, &task);
1523 if (result != kSuccess) {
1524 NSLog(@"Could not post a task to the Flutter engine.");
1525 }
1526 }
1527}
1528
1529- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1530 __weak FlutterEngine* weakSelf = self;
1531 auto worker = ^{
1532 [weakSelf runTaskOnEmbedder:task];
1533 };
1534
1535 const auto engine_time = _embedderAPI.GetCurrentTime();
1536 if (targetTime <= engine_time) {
1537 dispatch_async(dispatch_get_main_queue(), worker);
1538
1539 } else {
1540 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, targetTime - engine_time),
1541 dispatch_get_main_queue(), worker);
1542 }
1543}
1544
1545// Getter used by test harness, only exposed through the FlutterEngine(Test) category
1546- (flutter::FlutterCompositor*)macOSCompositor {
1547 return _macOSCompositor.get();
1548}
1549
1550@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
static void round(SkPoint *p)
static bool left(const SkPoint &p0, const SkPoint &p1)
static SkScalar center(float pos0, float pos1)
NSInteger clearContents()
std::vector< std::shared_ptr< FakeTexture > > textures
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
Definition embedder.cc:3329
#define FLUTTER_API_SYMBOL(symbol)
Definition embedder.h:67
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition embedder.h:2108
void(* FlutterPlatformMessageCallback)(const FlutterPlatformMessage *, void *)
Definition embedder.h:1175
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2004
FlutterThreadPriority
Valid values for priority of Thread.
Definition embedder.h:256
@ kDisplay
Suitable for threads which generate data for the display.
Definition embedder.h:262
@ kRaster
Suitable for thread which raster data.
Definition embedder.h:264
FlutterSemanticsAction
Definition embedder.h:113
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1153
FlutterEngineResult(* FlutterEngineSendPlatformMessageResponseFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessageResponseHandle *handle, const uint8_t *data, size_t data_length)
Definition embedder.h:3238
#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)
NSObject< FlutterBinaryMessenger > * binaryMessenger
void engineCallbackOnPlatformMessage:(const FlutterPlatformMessage *message)
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
FlutterMethodChannel * _platformViewsChannel
_FlutterEngineAOTData * _aotData
std::unique_ptr< flutter::FlutterCompositor > _macOSCompositor
static const int kMainThreadPriority
FlutterPlatformViewController * _platformViewController
static void OnPlatformMessage(const FlutterPlatformMessage *message, FlutterEngine *engine)
FlutterBasicMessageChannel * _accessibilityChannel
FlutterBasicMessageChannel * _settingsChannel
static FlutterLocale FlutterLocaleFromNSLocale(NSLocale *locale)
BOOL _allowHeadlessExecution
NSMutableDictionary< NSString *, FlutterEngineHandlerInfo * > * _messengerHandlers
FlutterBinaryMessengerConnection _currentMessengerConnection
FlutterMethodChannel * _platformChannel
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
FlutterBinaryMessengerRelay * _binaryMessenger
static NSString *const kEnhancedUserInterfaceKey
NSString *const kFlutterSettingsChannel
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)
call(args)
Definition dom.py:159
constexpr int64_t kFlutterImplicitViewId
Definition constants.h:35
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
std::vector< std::string > GetSwitchesFromEnvironment()
Definition ref_ptr.h:256
int32_t width
FlutterBackingStoreCreateCallback create_backing_store_callback
Definition embedder.h:1901
bool avoid_backing_store_cache
Avoid caching backing stores provided by this compositor.
Definition embedder.h:1921
size_t struct_size
This size of this struct. Must be sizeof(FlutterCompositor).
Definition embedder.h:1885
FlutterPresentViewCallback present_view_callback
Definition embedder.h:1930
FlutterBackingStoreCollectCallback collect_backing_store_callback
Definition embedder.h:1906
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
Definition embedder.h:1589
size_t height
The height of the display, in physical pixels.
Definition embedder.h:1989
size_t struct_size
This size of this struct. Must be sizeof(FlutterDisplay).
Definition embedder.h:1971
size_t width
The width of the display, in physical pixels.
Definition embedder.h:1986
FlutterEngineDisplayId display_id
Definition embedder.h:1973
Function-pointer-based versions of the APIs above.
Definition embedder.h:3317
const char * language_code
Definition embedder.h:1939
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:1935
const char * script_code
Definition embedder.h:1949
const char * country_code
Definition embedder.h:1944
const char * variant_code
Definition embedder.h:1954
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1162
FlutterPlatformMessageCallback platform_message_callback
Definition embedder.h:2190
FlutterLogMessageCallback log_message_callback
Definition embedder.h:2382
VsyncCallback vsync_callback
Definition embedder.h:2287
const char * assets_path
Definition embedder.h:2142
OnPreEngineRestartCallback on_pre_engine_restart_callback
Definition embedder.h:2399
FlutterEngineAOTData aot_data
Definition embedder.h:2351
const char *const * dart_entrypoint_argv
Definition embedder.h:2374
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2138
FlutterUpdateSemanticsCallback2 update_semantics_callback2
Definition embedder.h:2429
const char *const * command_line_argv
Definition embedder.h:2184
const char * icu_data_path
Definition embedder.h:2166
bool shutdown_dart_vm_when_done
Definition embedder.h:2320
const char * custom_dart_entrypoint
Definition embedder.h:2296
const FlutterCustomTaskRunners * custom_task_runners
Definition embedder.h:2301
int command_line_argc
The command line argument count used to initialize the project.
Definition embedder.h:2168
VoidCallback root_isolate_create_callback
Definition embedder.h:2225
const FlutterCompositor * compositor
Definition embedder.h:2336
A batch of updates to semantics nodes and custom actions.
Definition embedder.h:1502
size_t struct_size
The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
Definition embedder.h:1564
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:841
const uintptr_t id
int BOOL