14 #include "flutter/shell/platform/embedder/embedder.h"
39 FlutterLocale flutterLocale = {};
40 flutterLocale.struct_size =
sizeof(FlutterLocale);
41 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
42 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
43 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
44 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
50 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
62 - (instancetype)initWithConnection:(NSNumber*)connection
71 - (instancetype)initWithConnection:(NSNumber*)connection
74 NSAssert(
self,
@"Super init cannot be nil");
93 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
98 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
103 @property(nonatomic, readonly)
104 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
129 - (void)shutDownIfNeeded;
134 - (void)sendUserLocales;
139 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
147 - (void)engineCallbackOnPreEngineRestart;
153 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
159 - (void)loadAOTData:(NSString*)assetsDir;
164 - (void)setUpPlatformViewChannel;
169 - (void)setUpAccessibilityChannel;
188 _acceptingRequests = NO;
190 _terminator = terminator ? terminator : ^(
id sender) {
193 [[NSApplication sharedApplication] terminate:sender];
195 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
196 if ([appDelegate respondsToSelector:
@selector(setTerminationHandler:)]) {
198 flutterAppDelegate.terminationHandler =
self;
205 - (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*,
id>*)arguments
207 NSString* type = arguments[@"type"];
213 FlutterAppExitType exitType =
214 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
223 - (void)requestApplicationTermination:(
id)sender
224 exitType:(FlutterAppExitType)type
226 _shouldTerminate = YES;
227 if (![
self acceptingRequests]) {
230 type = kFlutterAppExitTypeRequired;
233 case kFlutterAppExitTypeCancelable: {
237 [_engine sendOnChannel:kFlutterPlatformChannel
238 message:[codec encodeMethodCall:methodCall]
239 binaryReply:^(NSData* _Nullable reply) {
240 NSAssert(_terminator, @"terminator shouldn't be nil");
241 id decoded_reply = [codec decodeEnvelope:reply];
242 if ([decoded_reply isKindOfClass:[
FlutterError class]]) {
244 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
249 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
250 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
255 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
256 if ([replyArgs[@"response"] isEqual:@"exit"]) {
258 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
259 _shouldTerminate = NO;
267 case kFlutterAppExitTypeRequired:
268 NSAssert(
_terminator,
@"terminator shouldn't be nil");
281 return [[NSPasteboard generalPasteboard] clearContents];
284 - (NSString*)stringForType:(NSPasteboardType)dataType {
285 return [[NSPasteboard generalPasteboard] stringForType:dataType];
288 - (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
289 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
300 - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
314 NSString* _pluginKey;
320 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
323 _pluginKey = [pluginKey copy];
325 _publishedValue = [NSNull null];
330 #pragma mark - FlutterPluginRegistrar
341 return [
self viewForId:kFlutterImplicitViewId];
346 if (controller == nil) {
349 if (!controller.viewLoaded) {
350 [controller loadView];
352 return controller.flutterView;
355 - (void)addMethodCallDelegate:(nonnull
id<
FlutterPlugin>)delegate
363 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
365 id<FlutterAppLifecycleProvider> lifeCycleProvider =
366 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
367 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
368 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
373 withId:(nonnull NSString*)factoryId {
374 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
377 - (void)publish:(NSObject*)value {
378 _publishedValue = value;
381 - (NSString*)lookupKeyForAsset:(NSString*)asset {
385 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
392 #pragma mark - Static methods provided to engine configuration
395 [engine engineCallbackOnPlatformMessage:message];
464 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
465 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
468 - (instancetype)initWithName:(NSString*)labelPrefix
470 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
472 NSAssert(
self,
@"Super init cannot be nil");
479 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
480 _pluginRegistrars = [[NSMutableDictionary alloc] init];
483 _semanticsEnabled = NO;
485 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
486 [_isResponseValid addObject:@YES];
490 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
491 FlutterEngineGetProcAddresses(&_embedderAPI);
496 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
497 [notificationCenter addObserver:self
498 selector:@selector(sendUserLocales)
499 name:NSCurrentLocaleDidChangeNotification
504 [
self setUpPlatformViewChannel];
505 [
self setUpAccessibilityChannel];
506 [
self setUpNotificationCenterListeners];
507 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
511 id<FlutterAppLifecycleProvider> lifecycleProvider =
512 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
513 [lifecycleProvider addApplicationLifecycleDelegate:self];
515 _terminationHandler = nil;
522 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
524 id<FlutterAppLifecycleProvider> lifecycleProvider =
525 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
526 [lifecycleProvider removeApplicationLifecycleDelegate:self];
531 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
533 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
539 for (NSString* pluginName in _pluginRegistrars) {
540 [_pluginRegistrars[pluginName] publish:[NSNull null]];
542 @
synchronized(_isResponseValid) {
543 [_isResponseValid removeAllObjects];
544 [_isResponseValid addObject:@NO];
546 [
self shutDownEngine];
548 _embedderAPI.CollectAOTData(
_aotData);
552 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
558 NSLog(
@"Attempted to run an engine with no view controller without headless mode enabled.");
562 [
self addInternalPlugins];
565 std::vector<const char*> argv = {[
self.executableName UTF8String]};
566 std::vector<std::string> switches =
self.switches;
570 std::find(switches.begin(), switches.end(),
"--enable-impeller=true") != switches.end()) {
571 switches.push_back(
"--enable-impeller=true");
574 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
575 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
577 std::vector<const char*> dartEntrypointArgs;
578 for (NSString* argument in [
_project dartEntrypointArguments]) {
579 dartEntrypointArgs.push_back([argument UTF8String]);
582 FlutterProjectArgs flutterArguments = {};
583 flutterArguments.struct_size =
sizeof(FlutterProjectArgs);
584 flutterArguments.assets_path =
_project.assetsPath.UTF8String;
585 flutterArguments.icu_data_path =
_project.ICUDataPath.UTF8String;
586 flutterArguments.command_line_argc =
static_cast<int>(argv.size());
587 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
588 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)
OnPlatformMessage;
589 flutterArguments.update_semantics_callback2 = [](
const FlutterSemanticsUpdate2* update,
595 [[engine viewControllerForId:kFlutterImplicitViewId] updateSemantics:update];
597 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
598 flutterArguments.shutdown_dart_vm_when_done =
true;
599 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
600 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
601 flutterArguments.root_isolate_create_callback =
_project.rootIsolateCreateCallback;
602 flutterArguments.log_message_callback = [](
const char* tag,
const char* message,
605 std::cout << tag <<
": ";
607 std::cout << message << std::endl;
610 static size_t sTaskRunnerIdentifiers = 0;
611 const FlutterTaskRunnerDescription cocoa_task_runner_description = {
612 .struct_size =
sizeof(FlutterTaskRunnerDescription),
613 .
user_data = (
void*)CFBridgingRetain(
self),
614 .runs_task_on_current_thread_callback = [](
void*
user_data) ->
bool {
615 return [[NSThread currentThread] isMainThread];
617 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
620 targetTimeInNanoseconds:target_time_nanos];
622 .identifier = ++sTaskRunnerIdentifiers,
624 const FlutterCustomTaskRunners custom_task_runners = {
625 .struct_size =
sizeof(FlutterCustomTaskRunners),
626 .platform_task_runner = &cocoa_task_runner_description,
628 flutterArguments.custom_task_runners = &custom_task_runners;
630 [
self loadAOTData:_project.assetsPath];
632 flutterArguments.aot_data =
_aotData;
635 flutterArguments.compositor = [
self createFlutterCompositor];
637 flutterArguments.on_pre_engine_restart_callback = [](
void*
user_data) {
639 [engine engineCallbackOnPreEngineRestart];
642 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
643 FlutterEngineResult result = _embedderAPI.Initialize(
644 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge
void*)(
self), &_engine);
645 if (result != kSuccess) {
646 NSLog(
@"Failed to initialize Flutter engine: error %d", result);
650 result = _embedderAPI.RunInitialized(_engine);
651 if (result != kSuccess) {
652 NSLog(
@"Failed to run an initialized engine: error %d", result);
656 [
self sendUserLocales];
659 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
661 while ((nextViewController = [viewControllerEnumerator nextObject])) {
662 [
self updateWindowMetricsForViewController:nextViewController];
665 [
self updateDisplayConfig];
668 [
self sendInitialSettings];
672 - (void)loadAOTData:(NSString*)assetsDir {
673 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
677 BOOL isDirOut =
false;
678 NSFileManager* fileManager = [NSFileManager defaultManager];
682 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
684 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
688 FlutterEngineAOTDataSource source = {};
689 source.type = kFlutterEngineAOTDataSourceTypeElfPath;
690 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
692 auto result = _embedderAPI.CreateAOTData(&source, &
_aotData);
693 if (result != kSuccess) {
694 NSLog(
@"Failed to load AOT data from: %@", elfPath);
699 NSAssert(controller != nil,
@"The controller must not be nil.");
700 NSAssert(![controller attached],
701 @"The incoming view controller is already attached to an engine.");
702 NSAssert([
_viewControllers objectForKey:@(viewId)] == nil,
@"The requested view ID is occupied.");
703 [controller setUpWithEngine:self viewId:viewId threadSynchronizer:_threadSynchronizer];
704 NSAssert(controller.viewId == viewId,
@"Failed to assign view ID.");
705 [_viewControllers setObject:controller forKey:@(viewId)];
708 - (void)deregisterViewControllerForId:(
FlutterViewId)viewId {
710 if (oldController != nil) {
711 [oldController detachFromEngine];
712 [_viewControllers removeObjectForKey:@(viewId)];
716 - (void)shutDownIfNeeded {
718 [
self shutDownEngine];
724 NSAssert(controller == nil || controller.viewId == viewId,
725 @"The stored controller has unexpected view ID.");
731 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
732 if (currentController == controller) {
736 if (currentController == nil && controller != nil) {
738 NSAssert(controller.
engine == nil,
739 @"Failed to set view controller to the engine: "
740 @"The given FlutterViewController is already attached to an engine %@. "
741 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
742 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
744 [
self registerViewController:controller forId:kFlutterImplicitViewId];
745 }
else if (currentController != nil && controller == nil) {
747 @"The default controller has an unexpected ID %llu", currentController.viewId);
749 [
self deregisterViewControllerForId:kFlutterImplicitViewId];
750 [
self shutDownIfNeeded];
754 @"Failed to set view controller to the engine: "
755 @"The engine already has an implicit view controller %@. "
756 @"If you wanted to make the implicit view render in a different window, "
757 @"you should attach the current view controller to the window instead.",
763 return [
self viewControllerForId:kFlutterImplicitViewId];
766 - (FlutterCompositor*)createFlutterCompositor {
771 _compositor.struct_size =
sizeof(FlutterCompositor);
774 _compositor.create_backing_store_callback = [](
const FlutterBackingStoreConfig* config,
775 FlutterBackingStore* backing_store_out,
779 config, backing_store_out);
782 _compositor.collect_backing_store_callback = [](
const FlutterBackingStore* backing_store,
786 _compositor.present_layers_callback = [](
const FlutterLayer** layers,
794 layers, layers_count);
806 #pragma mark - Framework-internal methods
809 [
self registerViewController:controller forId:kFlutterImplicitViewId];
813 NSAssert([viewController attached] && viewController.
engine ==
self,
814 @"The given view controller is not associated with this engine.");
815 [
self deregisterViewControllerForId:viewController.viewId];
816 [
self shutDownIfNeeded];
820 return _engine !=
nullptr;
823 - (void)updateDisplayConfig:(NSNotification*)notification {
824 [
self updateDisplayConfig];
827 - (void)updateDisplayConfig {
832 std::vector<FlutterEngineDisplay> displays;
833 for (NSScreen* screen : [NSScreen screens]) {
834 CGDirectDisplayID displayID =
835 static_cast<CGDirectDisplayID
>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
837 FlutterEngineDisplay display;
838 display.struct_size =
sizeof(display);
839 display.display_id = displayID;
840 display.single_display =
false;
841 display.width =
static_cast<size_t>(screen.frame.size.width);
842 display.height =
static_cast<size_t>(screen.frame.size.height);
843 display.device_pixel_ratio = screen.backingScaleFactor;
845 CVDisplayLinkRef displayLinkRef = nil;
846 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
849 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
850 if (!(nominal.flags & kCVTimeIsIndefinite)) {
851 double refreshRate =
static_cast<double>(nominal.timeScale) / nominal.timeValue;
852 display.refresh_rate = round(refreshRate);
854 CVDisplayLinkRelease(displayLinkRef);
856 display.refresh_rate = 0;
859 displays.push_back(display);
861 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
862 displays.data(), displays.size());
865 - (void)onSettingsChanged:(NSNotification*)notification {
867 NSString* brightness =
868 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
869 [_settingsChannel sendMessage:@{
870 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
872 @"textScaleFactor" : @1.0,
873 @"alwaysUse24HourFormat" : @false
877 - (void)sendInitialSettings {
879 [[NSDistributedNotificationCenter defaultCenter]
881 selector:@selector(onSettingsChanged:)
882 name:@"AppleInterfaceThemeChangedNotification"
884 [
self onSettingsChanged:nil];
887 - (FlutterEngineProcTable&)embedderAPI {
891 - (nonnull NSString*)executableName {
892 return [[[NSProcessInfo processInfo] arguments] firstObject] ?:
@"Flutter";
902 if (!_engine || !viewController || !viewController.viewLoaded) {
905 NSAssert([
self viewControllerForId:viewController.viewId] == viewController,
906 @"The provided view controller is not attached to this engine.");
907 NSView* view = viewController.flutterView;
908 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
909 CGSize scaledSize = scaledBounds.size;
910 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
911 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
912 const FlutterWindowMetricsEvent windowMetricsEvent = {
913 .struct_size =
sizeof(windowMetricsEvent),
914 .width =
static_cast<size_t>(scaledSize.width),
915 .height =
static_cast<size_t>(scaledSize.height),
916 .pixel_ratio = pixelRatio,
917 .left =
static_cast<size_t>(scaledBounds.origin.x),
918 .top =
static_cast<size_t>(scaledBounds.origin.y),
919 .display_id =
static_cast<uint64_t
>(displayId),
921 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
924 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
925 _embedderAPI.SendPointerEvent(_engine, &event, 1);
928 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
929 callback:(FlutterKeyEventCallback)callback
930 userData:(
void*)userData {
931 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
934 - (void)setSemanticsEnabled:(BOOL)enabled {
935 if (_semanticsEnabled == enabled) {
938 _semanticsEnabled = enabled;
941 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
943 while ((nextViewController = [viewControllerEnumerator nextObject])) {
944 [nextViewController notifySemanticsEnabledChanged];
947 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
950 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
951 toTarget:(uint16_t)target
952 withData:(fml::MallocMapping)data {
953 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
960 #pragma mark - Private methods
962 - (void)sendUserLocales {
968 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
969 std::vector<FlutterLocale> flutterLocales;
970 flutterLocales.reserve(locales.count);
971 for (NSString* localeID in [NSLocale preferredLanguages]) {
972 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
973 [locales addObject:locale];
977 std::vector<const FlutterLocale*> flutterLocaleList;
978 flutterLocaleList.reserve(flutterLocales.size());
979 std::transform(flutterLocales.begin(), flutterLocales.end(),
980 std::back_inserter(flutterLocaleList),
981 [](
const auto& arg) ->
const auto* { return &arg; });
982 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
985 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
986 NSData* messageData = nil;
987 if (message->message_size > 0) {
988 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
989 length:message->message_size
992 NSString* channel = @(message->channel);
993 __block
const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
995 NSMutableArray* isResponseValid =
self.isResponseValid;
996 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
997 _embedderAPI.SendPlatformMessageResponse;
999 @
synchronized(isResponseValid) {
1000 if (![isResponseValid[0] boolValue]) {
1004 if (responseHandle) {
1005 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1006 static_cast<const uint8_t*
>(response.bytes), response.length);
1007 responseHandle = NULL;
1009 NSLog(
@"Error: Message responses can be sent only once. Ignoring duplicate response "
1018 handlerInfo.
handler(messageData, binaryResponseHandler);
1020 binaryResponseHandler(nil);
1024 - (void)engineCallbackOnPreEngineRestart {
1025 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1027 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1035 - (void)shutDownEngine {
1036 if (_engine ==
nullptr) {
1040 [_threadSynchronizer shutdown];
1043 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1044 if (result != kSuccess) {
1045 NSLog(
@"Could not de-initialize the Flutter engine: error %d", result);
1049 CFRelease((CFTypeRef)
self);
1051 result = _embedderAPI.Shutdown(_engine);
1052 if (result != kSuccess) {
1053 NSLog(
@"Failed to shut down Flutter engine: error %d", result);
1058 - (void)setUpPlatformViewChannel {
1065 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1066 [[weakSelf platformViewController] handleMethodCall:call result:result];
1070 - (void)setUpAccessibilityChannel {
1076 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1077 [weakSelf handleAccessibilityEvent:message];
1080 - (void)setUpNotificationCenterListeners {
1081 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1083 [center addObserver:self
1084 selector:@selector(onAccessibilityStatusChanged:)
1085 name:kEnhancedUserInterfaceNotification
1087 [center addObserver:self
1088 selector:@selector(applicationWillTerminate:)
1089 name:NSApplicationWillTerminateNotification
1091 [center addObserver:self
1092 selector:@selector(windowDidChangeScreen:)
1093 name:NSWindowDidChangeScreenNotification
1095 [center addObserver:self
1096 selector:@selector(updateDisplayConfig:)
1097 name:NSApplicationDidChangeScreenParametersNotification
1101 - (void)addInternalPlugins {
1113 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1114 [weakSelf handleMethodCall:call result:result];
1118 - (void)applicationWillTerminate:(NSNotification*)notification {
1119 [
self shutDownEngine];
1122 - (void)windowDidChangeScreen:(NSNotification*)notification {
1125 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1127 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1128 [
self updateWindowMetricsForViewController:nextViewController];
1132 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1133 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1134 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1136 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1140 self.semanticsEnabled = enabled;
1142 - (void)handleAccessibilityEvent:(NSDictionary<NSString*,
id>*)annotatedEvent {
1143 NSString* type = annotatedEvent[@"type"];
1144 if ([type isEqualToString:
@"announce"]) {
1145 NSString* message = annotatedEvent[@"data"][@"message"];
1146 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1147 if (message == nil) {
1151 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1152 ? NSAccessibilityPriorityHigh
1153 : NSAccessibilityPriorityMedium;
1155 [
self announceAccessibilityMessage:message withPriority:priority];
1159 - (void)announceAccessibilityMessage:(NSString*)message
1160 withPriority:(NSAccessibilityPriorityLevel)priority {
1161 NSAccessibilityPostNotificationWithUserInfo(
1163 NSAccessibilityAnnouncementRequestedNotification,
1164 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1167 if ([call.
method isEqualToString:
@"SystemNavigator.pop"]) {
1168 [[NSApplication sharedApplication] terminate:self];
1170 }
else if ([call.
method isEqualToString:
@"SystemSound.play"]) {
1171 [
self playSystemSound:call.arguments];
1173 }
else if ([call.
method isEqualToString:
@"Clipboard.getData"]) {
1174 result([
self getClipboardData:call.
arguments]);
1175 }
else if ([call.
method isEqualToString:
@"Clipboard.setData"]) {
1176 [
self setClipboardData:call.arguments];
1178 }
else if ([call.
method isEqualToString:
@"Clipboard.hasStrings"]) {
1179 result(@{
@"value" : @([
self clipboardHasStrings])});
1180 }
else if ([call.
method isEqualToString:
@"System.exitApplication"]) {
1181 if ([
self terminationHandler] == nil) {
1186 [NSApp terminate:self];
1189 [[
self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1191 }
else if ([call.
method isEqualToString:
@"System.initializationComplete"]) {
1192 if ([
self terminationHandler] != nil) {
1193 [
self terminationHandler].acceptingRequests = YES;
1201 - (void)playSystemSound:(NSString*)soundType {
1202 if ([soundType isEqualToString:
@"SystemSoundType.alert"]) {
1207 - (NSDictionary*)getClipboardData:(NSString*)format {
1209 NSString* stringInPasteboard = [
self.pasteboard stringForType:NSPasteboardTypeString];
1210 return stringInPasteboard == nil ? nil : @{
@"text" : stringInPasteboard};
1215 - (void)setClipboardData:(NSDictionary*)data {
1216 NSString* text = data[@"text"];
1217 [
self.pasteboard clearContents];
1218 if (text && ![text isEqual:[NSNull
null]]) {
1219 [
self.pasteboard setString:text forType:NSPasteboardTypeString];
1223 - (BOOL)clipboardHasStrings {
1224 return [
self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1227 - (std::vector<std::string>)switches {
1235 #pragma mark - FlutterAppLifecycleDelegate
1238 NSString* nextState =
1239 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1240 [
self sendOnChannel:kFlutterLifecycleChannel
1241 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1248 - (void)handleWillBecomeActive:(NSNotification*)notification {
1251 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1253 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1261 - (void)handleWillResignActive:(NSNotification*)notification {
1264 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1266 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1274 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1275 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1276 if (occlusionState & NSApplicationOcclusionStateVisible) {
1279 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1281 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1285 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1289 #pragma mark - FlutterBinaryMessenger
1291 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1292 [
self sendOnChannel:channel message:message binaryReply:nil];
1295 - (void)sendOnChannel:(NSString*)channel
1296 message:(NSData* _Nullable)message
1298 FlutterPlatformMessageResponseHandle* response_handle =
nullptr;
1303 auto captures = std::make_unique<Captures>();
1304 captures->reply = callback;
1305 auto message_reply = [](
const uint8_t* data,
size_t data_size,
void*
user_data) {
1306 auto captures =
reinterpret_cast<Captures*
>(
user_data);
1307 NSData* reply_data = nil;
1308 if (data !=
nullptr && data_size > 0) {
1309 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1311 captures->reply(reply_data);
1315 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1316 _engine, message_reply, captures.get(), &response_handle);
1317 if (create_result != kSuccess) {
1318 NSLog(
@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1324 FlutterPlatformMessage platformMessage = {
1325 .struct_size =
sizeof(FlutterPlatformMessage),
1326 .channel = [channel UTF8String],
1327 .message =
static_cast<const uint8_t*
>(message.bytes),
1328 .message_size = message.length,
1329 .response_handle = response_handle,
1332 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1333 if (message_result != kSuccess) {
1334 NSLog(
@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1338 if (response_handle !=
nullptr) {
1339 FlutterEngineResult release_result =
1340 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1341 if (release_result != kSuccess) {
1342 NSLog(
@"Failed to release the response handle (%d).", release_result);
1348 binaryMessageHandler:
1353 handler:[handler copy]];
1360 NSString* foundChannel = nil;
1363 if ([handlerInfo.
connection isEqual:@(connection)]) {
1369 [_messengerHandlers removeObjectForKey:foundChannel];
1373 #pragma mark - FlutterPluginRegistry
1376 id<FlutterPluginRegistrar> registrar =
self.pluginRegistrars[pluginName];
1380 self.pluginRegistrars[pluginName] = registrarImpl;
1381 registrar = registrarImpl;
1386 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1390 #pragma mark - FlutterTextureRegistrar
1393 return [_renderer registerTexture:texture];
1396 - (BOOL)registerTextureWithID:(int64_t)textureId {
1397 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1400 - (void)textureFrameAvailable:(int64_t)textureID {
1401 [_renderer textureFrameAvailable:textureID];
1404 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1405 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1408 - (void)unregisterTexture:(int64_t)textureID {
1409 [_renderer unregisterTexture:textureID];
1412 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1413 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1416 #pragma mark - Task runner integration
1418 - (void)runTaskOnEmbedder:(FlutterTask)task {
1420 auto result = _embedderAPI.RunTask(_engine, &task);
1421 if (result != kSuccess) {
1422 NSLog(
@"Could not post a task to the Flutter engine.");
1427 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1430 [weakSelf runTaskOnEmbedder:task];
1433 const auto engine_time = _embedderAPI.GetCurrentTime();
1434 if (targetTime <= engine_time) {
1435 dispatch_async(dispatch_get_main_queue(), worker);
1438 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, targetTime - engine_time),
1439 dispatch_get_main_queue(), worker);
1444 - (
flutter::FlutterCompositor*)macOSCompositor {