14 #include "flutter/shell/platform/embedder/embedder.h"
42 FlutterLocale flutterLocale = {};
43 flutterLocale.struct_size =
sizeof(FlutterLocale);
44 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
45 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
46 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
47 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
53 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
65 - (instancetype)initWithConnection:(NSNumber*)connection
74 - (instancetype)initWithConnection:(NSNumber*)connection
77 NSAssert(
self,
@"Super init cannot be nil");
96 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
101 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
106 @property(nonatomic, readonly)
107 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
132 - (void)shutDownIfNeeded;
137 - (void)sendUserLocales;
142 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
150 - (void)engineCallbackOnPreEngineRestart;
156 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
162 - (void)loadAOTData:(NSString*)assetsDir;
167 - (void)setUpPlatformViewChannel;
172 - (void)setUpAccessibilityChannel;
191 _acceptingRequests = NO;
193 _terminator = terminator ? terminator : ^(
id sender) {
196 [[NSApplication sharedApplication] terminate:sender];
198 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
199 if ([appDelegate respondsToSelector:
@selector(setTerminationHandler:)]) {
201 flutterAppDelegate.terminationHandler =
self;
208 - (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*,
id>*)arguments
210 NSString* type = arguments[@"type"];
216 FlutterAppExitType exitType =
217 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
226 - (void)requestApplicationTermination:(
id)sender
227 exitType:(FlutterAppExitType)type
229 _shouldTerminate = YES;
230 if (![
self acceptingRequests]) {
233 type = kFlutterAppExitTypeRequired;
236 case kFlutterAppExitTypeCancelable: {
240 [_engine sendOnChannel:kFlutterPlatformChannel
241 message:[codec encodeMethodCall:methodCall]
242 binaryReply:^(NSData* _Nullable reply) {
243 NSAssert(_terminator, @"terminator shouldn't be nil");
244 id decoded_reply = [codec decodeEnvelope:reply];
245 if ([decoded_reply isKindOfClass:[
FlutterError class]]) {
247 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
252 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
253 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
258 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
259 if ([replyArgs[@"response"] isEqual:@"exit"]) {
261 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
262 _shouldTerminate = NO;
270 case kFlutterAppExitTypeRequired:
271 NSAssert(
_terminator,
@"terminator shouldn't be nil");
284 return [[NSPasteboard generalPasteboard] clearContents];
287 - (NSString*)stringForType:(NSPasteboardType)dataType {
288 return [[NSPasteboard generalPasteboard] stringForType:dataType];
291 - (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
292 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
303 - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
317 NSString* _pluginKey;
323 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
326 _pluginKey = [pluginKey copy];
328 _publishedValue = [NSNull null];
333 #pragma mark - FlutterPluginRegistrar
344 return [
self viewForId:kFlutterImplicitViewId];
349 if (controller == nil) {
352 if (!controller.viewLoaded) {
353 [controller loadView];
355 return controller.flutterView;
358 - (void)addMethodCallDelegate:(nonnull
id<
FlutterPlugin>)delegate
366 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
368 id<FlutterAppLifecycleProvider> lifeCycleProvider =
369 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
370 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
371 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
376 withId:(nonnull NSString*)factoryId {
377 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
380 - (void)publish:(NSObject*)value {
381 _publishedValue = value;
384 - (NSString*)lookupKeyForAsset:(NSString*)asset {
388 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
395 #pragma mark - Static methods provided to engine configuration
398 [engine engineCallbackOnPlatformMessage:message];
471 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
472 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
477 static void SetThreadPriority(FlutterThreadPriority priority) {
478 if (priority == kDisplay || priority == kRaster) {
479 pthread_t thread = pthread_self();
482 if (!pthread_getschedparam(thread, &policy, ¶m)) {
484 pthread_setschedparam(thread, policy, ¶m);
486 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
490 - (instancetype)initWithName:(NSString*)labelPrefix
492 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
494 NSAssert(
self,
@"Super init cannot be nil");
501 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
502 _pluginRegistrars = [[NSMutableDictionary alloc] init];
505 _semanticsEnabled = NO;
507 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
508 [_isResponseValid addObject:@YES];
512 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
513 FlutterEngineGetProcAddresses(&_embedderAPI);
518 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
519 [notificationCenter addObserver:self
520 selector:@selector(sendUserLocales)
521 name:NSCurrentLocaleDidChangeNotification
526 [
self setUpPlatformViewChannel];
527 [
self setUpAccessibilityChannel];
528 [
self setUpNotificationCenterListeners];
529 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
533 id<FlutterAppLifecycleProvider> lifecycleProvider =
534 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
535 [lifecycleProvider addApplicationLifecycleDelegate:self];
537 _terminationHandler = nil;
546 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
548 id<FlutterAppLifecycleProvider> lifecycleProvider =
549 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
550 [lifecycleProvider removeApplicationLifecycleDelegate:self];
555 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
557 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
563 for (NSString* pluginName in _pluginRegistrars) {
564 [_pluginRegistrars[pluginName] publish:[NSNull null]];
566 @
synchronized(_isResponseValid) {
567 [_isResponseValid removeAllObjects];
568 [_isResponseValid addObject:@NO];
570 [
self shutDownEngine];
572 _embedderAPI.CollectAOTData(
_aotData);
576 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
582 NSLog(
@"Attempted to run an engine with no view controller without headless mode enabled.");
586 [
self addInternalPlugins];
589 std::vector<const char*> argv = {[
self.executableName UTF8String]};
590 std::vector<std::string> switches =
self.switches;
594 std::find(switches.begin(), switches.end(),
"--enable-impeller=true") != switches.end()) {
595 switches.push_back(
"--enable-impeller=true");
598 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
599 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
601 std::vector<const char*> dartEntrypointArgs;
602 for (NSString* argument in [
_project dartEntrypointArguments]) {
603 dartEntrypointArgs.push_back([argument UTF8String]);
606 FlutterProjectArgs flutterArguments = {};
607 flutterArguments.struct_size =
sizeof(FlutterProjectArgs);
608 flutterArguments.assets_path =
_project.assetsPath.UTF8String;
609 flutterArguments.icu_data_path =
_project.ICUDataPath.UTF8String;
610 flutterArguments.command_line_argc =
static_cast<int>(argv.size());
611 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
612 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)
OnPlatformMessage;
613 flutterArguments.update_semantics_callback2 = [](
const FlutterSemanticsUpdate2* update,
619 [[engine viewControllerForId:kFlutterImplicitViewId] updateSemantics:update];
621 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
622 flutterArguments.shutdown_dart_vm_when_done =
true;
623 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
624 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
625 flutterArguments.root_isolate_create_callback =
_project.rootIsolateCreateCallback;
626 flutterArguments.log_message_callback = [](
const char* tag,
const char* message,
629 std::cout << tag <<
": ";
631 std::cout << message << std::endl;
634 static size_t sTaskRunnerIdentifiers = 0;
635 const FlutterTaskRunnerDescription cocoa_task_runner_description = {
636 .struct_size =
sizeof(FlutterTaskRunnerDescription),
637 .
user_data = (
void*)CFBridgingRetain(
self),
638 .runs_task_on_current_thread_callback = [](
void*
user_data) ->
bool {
639 return [[NSThread currentThread] isMainThread];
641 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
644 targetTimeInNanoseconds:target_time_nanos];
646 .identifier = ++sTaskRunnerIdentifiers,
648 const FlutterCustomTaskRunners custom_task_runners = {
649 .struct_size =
sizeof(FlutterCustomTaskRunners),
650 .platform_task_runner = &cocoa_task_runner_description,
651 .thread_priority_setter = SetThreadPriority};
652 flutterArguments.custom_task_runners = &custom_task_runners;
654 [
self loadAOTData:_project.assetsPath];
656 flutterArguments.aot_data =
_aotData;
659 flutterArguments.compositor = [
self createFlutterCompositor];
661 flutterArguments.on_pre_engine_restart_callback = [](
void*
user_data) {
663 [engine engineCallbackOnPreEngineRestart];
666 flutterArguments.vsync_callback = [](
void*
user_data, intptr_t baton) {
668 [engine onVSync:baton];
671 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
672 FlutterEngineResult result = _embedderAPI.Initialize(
673 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge
void*)(
self), &_engine);
674 if (result != kSuccess) {
675 NSLog(
@"Failed to initialize Flutter engine: error %d", result);
679 result = _embedderAPI.RunInitialized(_engine);
680 if (result != kSuccess) {
681 NSLog(
@"Failed to run an initialized engine: error %d", result);
685 [
self sendUserLocales];
688 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
690 while ((nextViewController = [viewControllerEnumerator nextObject])) {
691 [
self updateWindowMetricsForViewController:nextViewController];
694 [
self updateDisplayConfig];
697 [
self sendInitialSettings];
701 - (void)loadAOTData:(NSString*)assetsDir {
702 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
706 BOOL isDirOut =
false;
707 NSFileManager* fileManager = [NSFileManager defaultManager];
711 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
713 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
717 FlutterEngineAOTDataSource source = {};
718 source.type = kFlutterEngineAOTDataSourceTypeElfPath;
719 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
721 auto result = _embedderAPI.CreateAOTData(&source, &
_aotData);
722 if (result != kSuccess) {
723 NSLog(
@"Failed to load AOT data from: %@", elfPath);
728 NSAssert(controller != nil,
@"The controller must not be nil.");
729 NSAssert(![controller attached],
730 @"The incoming view controller is already attached to an engine.");
731 NSAssert([
_viewControllers objectForKey:@(viewId)] == nil,
@"The requested view ID is occupied.");
732 [controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer];
733 NSAssert(controller.viewId == viewId,
@"Failed to assign view ID.");
734 [_viewControllers setObject:controller forKey:@(viewId)];
736 if (controller.viewLoaded) {
737 [
self viewControllerViewDidLoad:controller];
746 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
749 uint64_t targetTimeNanos =
751 FlutterEngine* engine = weakSelf;
757 [engine->_threadSynchronizer performOnPlatformThread:^{
758 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
762 FML_DCHECK([
_vsyncWaiters objectForKey:@(viewController.viewId)] == nil);
764 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewId)];
768 - (void)deregisterViewControllerForId:(
FlutterViewId)viewId {
770 if (oldController != nil) {
771 [oldController detachFromEngine];
772 [_viewControllers removeObjectForKey:@(viewId)];
775 [_vsyncWaiters removeObjectForKey:@(viewId)];
779 - (void)shutDownIfNeeded {
781 [
self shutDownEngine];
787 NSAssert(controller == nil || controller.viewId == viewId,
788 @"The stored controller has unexpected view ID.");
794 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
795 if (currentController == controller) {
799 if (currentController == nil && controller != nil) {
801 NSAssert(controller.
engine == nil,
802 @"Failed to set view controller to the engine: "
803 @"The given FlutterViewController is already attached to an engine %@. "
804 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
805 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
807 [
self registerViewController:controller forId:kFlutterImplicitViewId];
808 }
else if (currentController != nil && controller == nil) {
810 @"The default controller has an unexpected ID %llu", currentController.viewId);
812 [
self deregisterViewControllerForId:kFlutterImplicitViewId];
813 [
self shutDownIfNeeded];
817 @"Failed to set view controller to the engine: "
818 @"The engine already has an implicit view controller %@. "
819 @"If you wanted to make the implicit view render in a different window, "
820 @"you should attach the current view controller to the window instead.",
826 return [
self viewControllerForId:kFlutterImplicitViewId];
829 - (FlutterCompositor*)createFlutterCompositor {
835 _compositor.struct_size =
sizeof(FlutterCompositor);
838 _compositor.create_backing_store_callback = [](
const FlutterBackingStoreConfig* config,
839 FlutterBackingStore* backing_store_out,
843 config, backing_store_out);
846 _compositor.collect_backing_store_callback = [](
const FlutterBackingStore* backing_store,
850 _compositor.present_layers_callback = [](
const FlutterLayer** layers,
858 layers, layers_count);
870 #pragma mark - Framework-internal methods
873 [
self registerViewController:controller forId:kFlutterImplicitViewId];
877 NSAssert([viewController attached] && viewController.
engine ==
self,
878 @"The given view controller is not associated with this engine.");
879 [
self deregisterViewControllerForId:viewController.viewId];
880 [
self shutDownIfNeeded];
884 return _engine !=
nullptr;
887 - (void)updateDisplayConfig:(NSNotification*)notification {
888 [
self updateDisplayConfig];
891 - (NSArray<NSScreen*>*)screens {
892 return [NSScreen screens];
895 - (void)updateDisplayConfig {
900 std::vector<FlutterEngineDisplay> displays;
901 for (NSScreen* screen : [
self screens]) {
902 CGDirectDisplayID displayID =
903 static_cast<CGDirectDisplayID
>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
905 double devicePixelRatio = screen.backingScaleFactor;
906 FlutterEngineDisplay display;
907 display.struct_size =
sizeof(display);
908 display.display_id = displayID;
909 display.single_display =
false;
910 display.width =
static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
911 display.height =
static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
912 display.device_pixel_ratio = devicePixelRatio;
914 CVDisplayLinkRef displayLinkRef = nil;
915 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
918 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
919 if (!(nominal.flags & kCVTimeIsIndefinite)) {
920 double refreshRate =
static_cast<double>(nominal.timeScale) / nominal.timeValue;
921 display.refresh_rate = round(refreshRate);
923 CVDisplayLinkRelease(displayLinkRef);
925 display.refresh_rate = 0;
928 displays.push_back(display);
930 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
931 displays.data(), displays.size());
934 - (void)onSettingsChanged:(NSNotification*)notification {
936 NSString* brightness =
937 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
938 [_settingsChannel sendMessage:@{
939 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
941 @"textScaleFactor" : @1.0,
942 @"alwaysUse24HourFormat" : @false
946 - (void)sendInitialSettings {
948 [[NSDistributedNotificationCenter defaultCenter]
950 selector:@selector(onSettingsChanged:)
951 name:@"AppleInterfaceThemeChangedNotification"
953 [
self onSettingsChanged:nil];
956 - (FlutterEngineProcTable&)embedderAPI {
960 - (nonnull NSString*)executableName {
961 return [[[NSProcessInfo processInfo] arguments] firstObject] ?:
@"Flutter";
965 if (!_engine || !viewController || !viewController.viewLoaded) {
968 NSAssert([
self viewControllerForId:viewController.viewId] == viewController,
969 @"The provided view controller is not attached to this engine.");
970 NSView* view = viewController.flutterView;
971 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
972 CGSize scaledSize = scaledBounds.size;
973 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
974 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
975 const FlutterWindowMetricsEvent windowMetricsEvent = {
976 .struct_size =
sizeof(windowMetricsEvent),
977 .width =
static_cast<size_t>(scaledSize.width),
978 .height =
static_cast<size_t>(scaledSize.height),
979 .pixel_ratio = pixelRatio,
980 .left =
static_cast<size_t>(scaledBounds.origin.x),
981 .top =
static_cast<size_t>(scaledBounds.origin.y),
982 .display_id =
static_cast<uint64_t
>(displayId),
983 .view_id = viewController.viewId,
985 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
988 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
989 _embedderAPI.SendPointerEvent(_engine, &event, 1);
992 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
993 callback:(FlutterKeyEventCallback)callback
994 userData:(
void*)userData {
995 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
998 - (void)setSemanticsEnabled:(BOOL)enabled {
999 if (_semanticsEnabled == enabled) {
1002 _semanticsEnabled = enabled;
1005 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1007 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1008 [nextViewController notifySemanticsEnabledChanged];
1011 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1014 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1015 toTarget:(uint16_t)target
1016 withData:(fml::MallocMapping)data {
1017 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1024 #pragma mark - Private methods
1026 - (void)sendUserLocales {
1027 if (!
self.running) {
1032 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1033 std::vector<FlutterLocale> flutterLocales;
1034 flutterLocales.reserve(locales.count);
1035 for (NSString* localeID in [NSLocale preferredLanguages]) {
1036 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1037 [locales addObject:locale];
1041 std::vector<const FlutterLocale*> flutterLocaleList;
1042 flutterLocaleList.reserve(flutterLocales.size());
1043 std::transform(flutterLocales.begin(), flutterLocales.end(),
1044 std::back_inserter(flutterLocaleList),
1045 [](
const auto& arg) ->
const auto* { return &arg; });
1046 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1049 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1050 NSData* messageData = nil;
1051 if (message->message_size > 0) {
1052 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1053 length:message->message_size
1056 NSString* channel = @(message->channel);
1057 __block
const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1059 NSMutableArray* isResponseValid =
self.isResponseValid;
1060 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1061 _embedderAPI.SendPlatformMessageResponse;
1063 @
synchronized(isResponseValid) {
1064 if (![isResponseValid[0] boolValue]) {
1068 if (responseHandle) {
1069 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1070 static_cast<const uint8_t*
>(response.bytes), response.length);
1071 responseHandle = NULL;
1073 NSLog(
@"Error: Message responses can be sent only once. Ignoring duplicate response "
1082 handlerInfo.
handler(messageData, binaryResponseHandler);
1084 binaryResponseHandler(nil);
1088 - (void)engineCallbackOnPreEngineRestart {
1089 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1091 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1096 - (void)onVSync:(uintptr_t)baton {
1108 - (void)shutDownEngine {
1109 if (_engine ==
nullptr) {
1113 [_threadSynchronizer shutdown];
1116 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1117 if (result != kSuccess) {
1118 NSLog(
@"Could not de-initialize the Flutter engine: error %d", result);
1122 CFRelease((CFTypeRef)
self);
1124 result = _embedderAPI.Shutdown(_engine);
1125 if (result != kSuccess) {
1126 NSLog(
@"Failed to shut down Flutter engine: error %d", result);
1131 - (void)setUpPlatformViewChannel {
1138 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1139 [[weakSelf platformViewController] handleMethodCall:call result:result];
1143 - (void)setUpAccessibilityChannel {
1149 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1150 [weakSelf handleAccessibilityEvent:message];
1153 - (void)setUpNotificationCenterListeners {
1154 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1156 [center addObserver:self
1157 selector:@selector(onAccessibilityStatusChanged:)
1158 name:kEnhancedUserInterfaceNotification
1160 [center addObserver:self
1161 selector:@selector(applicationWillTerminate:)
1162 name:NSApplicationWillTerminateNotification
1164 [center addObserver:self
1165 selector:@selector(windowDidChangeScreen:)
1166 name:NSWindowDidChangeScreenNotification
1168 [center addObserver:self
1169 selector:@selector(updateDisplayConfig:)
1170 name:NSApplicationDidChangeScreenParametersNotification
1174 - (void)addInternalPlugins {
1186 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1187 [weakSelf handleMethodCall:call result:result];
1191 - (void)applicationWillTerminate:(NSNotification*)notification {
1192 [
self shutDownEngine];
1195 - (void)windowDidChangeScreen:(NSNotification*)notification {
1198 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1200 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1201 [
self updateWindowMetricsForViewController:nextViewController];
1205 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1206 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1207 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1209 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1213 self.semanticsEnabled = enabled;
1215 - (void)handleAccessibilityEvent:(NSDictionary<NSString*,
id>*)annotatedEvent {
1216 NSString* type = annotatedEvent[@"type"];
1217 if ([type isEqualToString:
@"announce"]) {
1218 NSString* message = annotatedEvent[@"data"][@"message"];
1219 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1220 if (message == nil) {
1224 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1225 ? NSAccessibilityPriorityHigh
1226 : NSAccessibilityPriorityMedium;
1228 [
self announceAccessibilityMessage:message withPriority:priority];
1232 - (void)announceAccessibilityMessage:(NSString*)message
1233 withPriority:(NSAccessibilityPriorityLevel)priority {
1234 NSAccessibilityPostNotificationWithUserInfo(
1236 NSAccessibilityAnnouncementRequestedNotification,
1237 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1240 if ([call.
method isEqualToString:
@"SystemNavigator.pop"]) {
1241 [[NSApplication sharedApplication] terminate:self];
1243 }
else if ([call.
method isEqualToString:
@"SystemSound.play"]) {
1244 [
self playSystemSound:call.arguments];
1246 }
else if ([call.
method isEqualToString:
@"Clipboard.getData"]) {
1247 result([
self getClipboardData:call.
arguments]);
1248 }
else if ([call.
method isEqualToString:
@"Clipboard.setData"]) {
1249 [
self setClipboardData:call.arguments];
1251 }
else if ([call.
method isEqualToString:
@"Clipboard.hasStrings"]) {
1252 result(@{
@"value" : @([
self clipboardHasStrings])});
1253 }
else if ([call.
method isEqualToString:
@"System.exitApplication"]) {
1254 if ([
self terminationHandler] == nil) {
1259 [NSApp terminate:self];
1262 [[
self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1264 }
else if ([call.
method isEqualToString:
@"System.initializationComplete"]) {
1265 if ([
self terminationHandler] != nil) {
1266 [
self terminationHandler].acceptingRequests = YES;
1274 - (void)playSystemSound:(NSString*)soundType {
1275 if ([soundType isEqualToString:
@"SystemSoundType.alert"]) {
1280 - (NSDictionary*)getClipboardData:(NSString*)format {
1282 NSString* stringInPasteboard = [
self.pasteboard stringForType:NSPasteboardTypeString];
1283 return stringInPasteboard == nil ? nil : @{
@"text" : stringInPasteboard};
1288 - (void)setClipboardData:(NSDictionary*)data {
1289 NSString* text = data[@"text"];
1290 [
self.pasteboard clearContents];
1291 if (text && ![text isEqual:[NSNull
null]]) {
1292 [
self.pasteboard setString:text forType:NSPasteboardTypeString];
1296 - (BOOL)clipboardHasStrings {
1297 return [
self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1300 - (std::vector<std::string>)switches {
1308 #pragma mark - FlutterAppLifecycleDelegate
1311 NSString* nextState =
1312 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1313 [
self sendOnChannel:kFlutterLifecycleChannel
1314 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1321 - (void)handleWillBecomeActive:(NSNotification*)notification {
1324 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1326 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1334 - (void)handleWillResignActive:(NSNotification*)notification {
1337 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1339 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1347 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1348 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1349 if (occlusionState & NSApplicationOcclusionStateVisible) {
1352 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1354 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1358 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1362 #pragma mark - FlutterBinaryMessenger
1364 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1365 [
self sendOnChannel:channel message:message binaryReply:nil];
1368 - (void)sendOnChannel:(NSString*)channel
1369 message:(NSData* _Nullable)message
1371 FlutterPlatformMessageResponseHandle* response_handle =
nullptr;
1376 auto captures = std::make_unique<Captures>();
1377 captures->reply = callback;
1378 auto message_reply = [](
const uint8_t* data,
size_t data_size,
void*
user_data) {
1379 auto captures =
reinterpret_cast<Captures*
>(
user_data);
1380 NSData* reply_data = nil;
1381 if (data !=
nullptr && data_size > 0) {
1382 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1384 captures->reply(reply_data);
1388 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1389 _engine, message_reply, captures.get(), &response_handle);
1390 if (create_result != kSuccess) {
1391 NSLog(
@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1397 FlutterPlatformMessage platformMessage = {
1398 .struct_size =
sizeof(FlutterPlatformMessage),
1399 .channel = [channel UTF8String],
1400 .message =
static_cast<const uint8_t*
>(message.bytes),
1401 .message_size = message.length,
1402 .response_handle = response_handle,
1405 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1406 if (message_result != kSuccess) {
1407 NSLog(
@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1411 if (response_handle !=
nullptr) {
1412 FlutterEngineResult release_result =
1413 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1414 if (release_result != kSuccess) {
1415 NSLog(
@"Failed to release the response handle (%d).", release_result);
1421 binaryMessageHandler:
1426 handler:[handler copy]];
1433 NSString* foundChannel = nil;
1436 if ([handlerInfo.
connection isEqual:@(connection)]) {
1442 [_messengerHandlers removeObjectForKey:foundChannel];
1446 #pragma mark - FlutterPluginRegistry
1449 id<FlutterPluginRegistrar> registrar =
self.pluginRegistrars[pluginName];
1453 self.pluginRegistrars[pluginName] = registrarImpl;
1454 registrar = registrarImpl;
1459 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1463 #pragma mark - FlutterTextureRegistrar
1466 return [_renderer registerTexture:texture];
1469 - (BOOL)registerTextureWithID:(int64_t)textureId {
1470 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1473 - (void)textureFrameAvailable:(int64_t)textureID {
1474 [_renderer textureFrameAvailable:textureID];
1477 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1478 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1481 - (void)unregisterTexture:(int64_t)textureID {
1482 [_renderer unregisterTexture:textureID];
1485 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1486 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1489 #pragma mark - Task runner integration
1491 - (void)runTaskOnEmbedder:(FlutterTask)task {
1493 auto result = _embedderAPI.RunTask(_engine, &task);
1494 if (result != kSuccess) {
1495 NSLog(
@"Could not post a task to the Flutter engine.");
1500 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1503 [weakSelf runTaskOnEmbedder:task];
1506 const auto engine_time = _embedderAPI.GetCurrentTime();
1507 if (targetTime <= engine_time) {
1508 dispatch_async(dispatch_get_main_queue(), worker);
1511 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, targetTime - engine_time),
1512 dispatch_get_main_queue(), worker);
1517 - (
flutter::FlutterCompositor*)macOSCompositor {