12 #include "flutter/common/constants.h"
15 #include "flutter/shell/platform/embedder/embedder.h"
39 using flutter::kFlutterImplicitViewId;
46 FlutterLocale flutterLocale = {};
47 flutterLocale.struct_size =
sizeof(FlutterLocale);
48 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
49 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
50 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
51 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
57 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
69 - (instancetype)initWithConnection:(NSNumber*)connection
78 - (instancetype)initWithConnection:(NSNumber*)connection
81 NSAssert(
self,
@"Super init cannot be nil");
103 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
108 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
113 @property(nonatomic, readonly)
114 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
141 - (void)shutDownIfNeeded;
146 - (void)sendUserLocales;
151 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
159 - (void)engineCallbackOnPreEngineRestart;
165 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
171 - (void)loadAOTData:(NSString*)assetsDir;
176 - (void)setUpPlatformViewChannel;
181 - (void)setUpAccessibilityChannel;
200 _acceptingRequests = NO;
202 _terminator = terminator ? terminator : ^(
id sender) {
205 [[NSApplication sharedApplication] terminate:sender];
207 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
208 if ([appDelegate respondsToSelector:
@selector(setTerminationHandler:)]) {
210 flutterAppDelegate.terminationHandler =
self;
217 - (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*,
id>*)arguments
219 NSString* type = arguments[@"type"];
225 FlutterAppExitType exitType =
226 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
228 [
self requestApplicationTermination:[NSApplication sharedApplication]
235 - (void)requestApplicationTermination:(
id)sender
236 exitType:(FlutterAppExitType)type
238 _shouldTerminate = YES;
239 if (![
self acceptingRequests]) {
242 type = kFlutterAppExitTypeRequired;
245 case kFlutterAppExitTypeCancelable: {
249 [_engine sendOnChannel:kFlutterPlatformChannel
250 message:[codec encodeMethodCall:methodCall]
251 binaryReply:^(NSData* _Nullable reply) {
252 NSAssert(_terminator, @"terminator shouldn't be nil");
253 id decoded_reply = [codec decodeEnvelope:reply];
254 if ([decoded_reply isKindOfClass:[
FlutterError class]]) {
256 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
261 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
262 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
267 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
268 if ([replyArgs[@"response"] isEqual:@"exit"]) {
270 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
271 _shouldTerminate = NO;
279 case kFlutterAppExitTypeRequired:
280 NSAssert(
_terminator,
@"terminator shouldn't be nil");
293 return [[NSPasteboard generalPasteboard] clearContents];
296 - (NSString*)stringForType:(NSPasteboardType)dataType {
297 return [[NSPasteboard generalPasteboard] stringForType:dataType];
300 - (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
301 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
312 - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
326 NSString* _pluginKey;
332 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
335 _pluginKey = [pluginKey copy];
337 _publishedValue = [NSNull null];
342 #pragma mark - FlutterPluginRegistrar
353 return [
self viewForIdentifier:kFlutterImplicitViewId];
358 if (controller == nil) {
361 if (!controller.viewLoaded) {
362 [controller loadView];
364 return controller.flutterView;
367 - (void)addMethodCallDelegate:(nonnull
id<
FlutterPlugin>)delegate
375 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
377 id<FlutterAppLifecycleProvider> lifeCycleProvider =
378 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
379 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
380 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
385 withId:(nonnull NSString*)factoryId {
386 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
389 - (void)publish:(NSObject*)value {
390 _publishedValue = value;
393 - (NSString*)lookupKeyForAsset:(NSString*)asset {
397 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
404 #pragma mark - Static methods provided to engine configuration
408 [engine engineCallbackOnPlatformMessage:message];
486 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
487 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
492 static void SetThreadPriority(FlutterThreadPriority priority) {
493 if (priority == kDisplay || priority == kRaster) {
494 pthread_t thread = pthread_self();
497 if (!pthread_getschedparam(thread, &policy, ¶m)) {
499 pthread_setschedparam(thread, policy, ¶m);
501 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
505 - (instancetype)initWithName:(NSString*)labelPrefix
507 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
509 NSAssert(
self,
@"Super init cannot be nil");
519 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
520 _pluginRegistrars = [[NSMutableDictionary alloc] init];
523 _semanticsEnabled = NO;
525 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
526 [_isResponseValid addObject:@YES];
530 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
531 FlutterEngineGetProcAddresses(&_embedderAPI);
536 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
537 [notificationCenter addObserver:self
538 selector:@selector(sendUserLocales)
539 name:NSCurrentLocaleDidChangeNotification
549 [
self setUpPlatformViewChannel];
550 [
self setUpAccessibilityChannel];
551 [
self setUpNotificationCenterListeners];
552 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
556 id<FlutterAppLifecycleProvider> lifecycleProvider =
557 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
558 [lifecycleProvider addApplicationLifecycleDelegate:self];
560 _terminationHandler = nil;
569 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
571 id<FlutterAppLifecycleProvider> lifecycleProvider =
572 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
573 [lifecycleProvider removeApplicationLifecycleDelegate:self];
578 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
580 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
586 for (NSString* pluginName in _pluginRegistrars) {
587 [_pluginRegistrars[pluginName] publish:[NSNull null]];
589 @
synchronized(_isResponseValid) {
590 [_isResponseValid removeAllObjects];
591 [_isResponseValid addObject:@NO];
593 [
self shutDownEngine];
595 _embedderAPI.CollectAOTData(
_aotData);
599 - (FlutterTaskRunnerDescription)createPlatformThreadTaskDescription {
600 static size_t sTaskRunnerIdentifiers = 0;
601 FlutterTaskRunnerDescription cocoa_task_runner_description = {
602 .struct_size =
sizeof(FlutterTaskRunnerDescription),
604 .
user_data = (__bridge_retained
void*)
self,
605 .runs_task_on_current_thread_callback = [](
void*
user_data) ->
bool {
606 return [[NSThread currentThread] isMainThread];
608 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
611 [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
613 .identifier = ++sTaskRunnerIdentifiers,
614 .destruction_callback =
621 return cocoa_task_runner_description;
624 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
630 NSLog(
@"Attempted to run an engine with no view controller without headless mode enabled.");
634 [
self addInternalPlugins];
637 std::vector<const char*> argv = {[
self.executableName UTF8String]};
638 std::vector<std::string> switches =
self.switches;
642 std::find(switches.begin(), switches.end(),
"--enable-impeller=true") != switches.end()) {
643 switches.push_back(
"--enable-impeller=true");
646 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
647 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
649 std::vector<const char*> dartEntrypointArgs;
650 for (NSString* argument in [
_project dartEntrypointArguments]) {
651 dartEntrypointArgs.push_back([argument UTF8String]);
654 FlutterProjectArgs flutterArguments = {};
655 flutterArguments.struct_size =
sizeof(FlutterProjectArgs);
656 flutterArguments.assets_path =
_project.assetsPath.UTF8String;
657 flutterArguments.icu_data_path =
_project.ICUDataPath.UTF8String;
658 flutterArguments.command_line_argc =
static_cast<int>(argv.size());
659 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
660 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)
OnPlatformMessage;
661 flutterArguments.update_semantics_callback2 = [](
const FlutterSemanticsUpdate2* update,
667 [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
669 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
670 flutterArguments.shutdown_dart_vm_when_done =
true;
671 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
672 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
673 flutterArguments.root_isolate_create_callback =
_project.rootIsolateCreateCallback;
674 flutterArguments.log_message_callback = [](
const char* tag,
const char* message,
677 std::cout << tag <<
": ";
679 std::cout << message << std::endl;
682 flutterArguments.engine_id =
reinterpret_cast<int64_t
>((__bridge
void*)
self);
684 BOOL mergedPlatformUIThread = NO;
685 NSNumber* enableMergedPlatformUIThread =
686 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
687 if (enableMergedPlatformUIThread != nil) {
688 mergedPlatformUIThread = enableMergedPlatformUIThread.boolValue;
691 if (mergedPlatformUIThread) {
692 NSLog(
@"Running with merged UI and platform thread. Experimental.");
698 FlutterTaskRunnerDescription platformTaskRunnerDescription =
699 [
self createPlatformThreadTaskDescription];
700 std::optional<FlutterTaskRunnerDescription> uiTaskRunnerDescription;
701 if (mergedPlatformUIThread) {
702 uiTaskRunnerDescription = [
self createPlatformThreadTaskDescription];
705 const FlutterCustomTaskRunners custom_task_runners = {
706 .struct_size =
sizeof(FlutterCustomTaskRunners),
707 .platform_task_runner = &platformTaskRunnerDescription,
708 .thread_priority_setter = SetThreadPriority,
709 .ui_task_runner = uiTaskRunnerDescription ? &uiTaskRunnerDescription.value() :
nullptr,
711 flutterArguments.custom_task_runners = &custom_task_runners;
713 [
self loadAOTData:_project.assetsPath];
715 flutterArguments.aot_data =
_aotData;
718 flutterArguments.compositor = [
self createFlutterCompositor];
720 flutterArguments.on_pre_engine_restart_callback = [](
void*
user_data) {
722 [engine engineCallbackOnPreEngineRestart];
725 flutterArguments.vsync_callback = [](
void*
user_data, intptr_t baton) {
727 [engine onVSync:baton];
730 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
731 FlutterEngineResult result = _embedderAPI.Initialize(
732 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge
void*)(
self), &_engine);
733 if (result != kSuccess) {
734 NSLog(
@"Failed to initialize Flutter engine: error %d", result);
738 result = _embedderAPI.RunInitialized(_engine);
739 if (result != kSuccess) {
740 NSLog(
@"Failed to run an initialized engine: error %d", result);
744 [
self sendUserLocales];
747 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
749 while ((nextViewController = [viewControllerEnumerator nextObject])) {
750 [
self updateWindowMetricsForViewController:nextViewController];
753 [
self updateDisplayConfig];
756 [
self sendInitialSettings];
760 - (void)loadAOTData:(NSString*)assetsDir {
761 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
765 BOOL isDirOut =
false;
766 NSFileManager* fileManager = [NSFileManager defaultManager];
770 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
772 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
776 FlutterEngineAOTDataSource source = {};
777 source.type = kFlutterEngineAOTDataSourceTypeElfPath;
778 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
780 auto result = _embedderAPI.CreateAOTData(&source, &
_aotData);
781 if (result != kSuccess) {
782 NSLog(
@"Failed to load AOT data from: %@", elfPath);
789 NSAssert(controller != nil,
@"The controller must not be nil.");
790 NSAssert(controller.
engine == nil,
791 @"The FlutterViewController is unexpectedly attached to "
792 @"engine %@ before initialization.",
795 @"The requested view ID is occupied.");
796 [_viewControllers setObject:controller forKey:@(viewIdentifier)];
797 [controller setUpWithEngine:self viewIdentifier:viewIdentifier];
798 NSAssert(controller.
viewIdentifier == viewIdentifier,
@"Failed to assign view ID.");
802 NSAssert(controller.
attached,
@"The FlutterViewController should switch to the attached mode "
803 @"after it is added to a FlutterEngine.");
804 NSAssert(controller.
engine ==
self,
805 @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
808 if (controller.viewLoaded) {
809 [
self viewControllerViewDidLoad:controller];
818 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
821 uint64_t targetTimeNanos =
823 FlutterEngine* engine = weakSelf;
825 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
830 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
839 if (controller != nil) {
840 [controller detachFromEngine];
842 @"The FlutterViewController unexpectedly stays attached after being removed. "
843 @"In unit tests, this is likely because either the FlutterViewController or "
844 @"the FlutterEngine is mocked. Please subclass these classes instead.");
846 [_viewControllers removeObjectForKey:@(viewIdentifier)];
848 [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
852 - (void)shutDownIfNeeded {
854 [
self shutDownEngine];
860 NSAssert(controller == nil || controller.
viewIdentifier == viewIdentifier,
861 @"The stored controller has unexpected view ID.");
867 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
868 if (currentController == controller) {
872 if (currentController == nil && controller != nil) {
874 NSAssert(controller.
engine == nil,
875 @"Failed to set view controller to the engine: "
876 @"The given FlutterViewController is already attached to an engine %@. "
877 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
878 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
880 [
self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
881 }
else if (currentController != nil && controller == nil) {
882 NSAssert(currentController.
viewIdentifier == kFlutterImplicitViewId,
883 @"The default controller has an unexpected ID %llu", currentController.
viewIdentifier);
885 [
self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
886 [
self shutDownIfNeeded];
890 @"Failed to set view controller to the engine: "
891 @"The engine already has an implicit view controller %@. "
892 @"If you wanted to make the implicit view render in a different window, "
893 @"you should attach the current view controller to the window instead.",
899 return [
self viewControllerForIdentifier:kFlutterImplicitViewId];
902 - (FlutterCompositor*)createFlutterCompositor {
904 _compositor.struct_size =
sizeof(FlutterCompositor);
907 _compositor.create_backing_store_callback = [](
const FlutterBackingStoreConfig* config,
908 FlutterBackingStore* backing_store_out,
912 config, backing_store_out);
915 _compositor.collect_backing_store_callback = [](
const FlutterBackingStore* backing_store,
919 _compositor.present_view_callback = [](
const FlutterPresentViewInfo* info) {
921 ->Present(info->view_id, info->layers, info->layers_count);
933 #pragma mark - Framework-internal methods
938 NSAssert(
self.viewController == nil,
939 @"The engine already has a view controller for the implicit view.");
940 self.viewController = controller;
944 [
self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
945 [
self shutDownIfNeeded];
949 return _engine !=
nullptr;
952 - (void)updateDisplayConfig:(NSNotification*)notification {
953 [
self updateDisplayConfig];
956 - (NSArray<NSScreen*>*)screens {
957 return [NSScreen screens];
960 - (void)updateDisplayConfig {
965 std::vector<FlutterEngineDisplay> displays;
966 for (NSScreen* screen : [
self screens]) {
967 CGDirectDisplayID displayID =
968 static_cast<CGDirectDisplayID
>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
970 double devicePixelRatio = screen.backingScaleFactor;
971 FlutterEngineDisplay display;
972 display.struct_size =
sizeof(display);
973 display.display_id = displayID;
974 display.single_display =
false;
975 display.width =
static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
976 display.height =
static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
977 display.device_pixel_ratio = devicePixelRatio;
979 CVDisplayLinkRef displayLinkRef = nil;
980 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
983 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
984 if (!(nominal.flags & kCVTimeIsIndefinite)) {
985 double refreshRate =
static_cast<double>(nominal.timeScale) / nominal.timeValue;
986 display.refresh_rate = round(refreshRate);
988 CVDisplayLinkRelease(displayLinkRef);
990 display.refresh_rate = 0;
993 displays.push_back(display);
995 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
996 displays.data(), displays.size());
999 - (void)onSettingsChanged:(NSNotification*)notification {
1001 NSString* brightness =
1002 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
1003 [_settingsChannel sendMessage:@{
1004 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
1006 @"textScaleFactor" : @1.0,
1011 - (void)sendInitialSettings {
1013 [[NSDistributedNotificationCenter defaultCenter]
1015 selector:@selector(onSettingsChanged:)
1016 name:@"AppleInterfaceThemeChangedNotification"
1018 [
self onSettingsChanged:nil];
1021 - (FlutterEngineProcTable&)embedderAPI {
1022 return _embedderAPI;
1025 - (nonnull NSString*)executableName {
1026 return [[[NSProcessInfo processInfo] arguments] firstObject] ?:
@"Flutter";
1030 if (!_engine || !viewController || !viewController.viewLoaded) {
1033 NSAssert([
self viewControllerForIdentifier:viewController.
viewIdentifier] == viewController,
1034 @"The provided view controller is not attached to this engine.");
1035 NSView* view = viewController.flutterView;
1036 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1037 CGSize scaledSize = scaledBounds.size;
1038 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
1039 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1040 const FlutterWindowMetricsEvent windowMetricsEvent = {
1041 .struct_size =
sizeof(windowMetricsEvent),
1042 .width =
static_cast<size_t>(scaledSize.width),
1043 .height =
static_cast<size_t>(scaledSize.height),
1044 .pixel_ratio = pixelRatio,
1045 .left =
static_cast<size_t>(scaledBounds.origin.x),
1046 .top =
static_cast<size_t>(scaledBounds.origin.y),
1047 .display_id =
static_cast<uint64_t
>(displayId),
1050 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1053 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1054 _embedderAPI.SendPointerEvent(_engine, &event, 1);
1058 - (void)setSemanticsEnabled:(BOOL)enabled {
1059 if (_semanticsEnabled == enabled) {
1062 _semanticsEnabled = enabled;
1065 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1067 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1068 [nextViewController notifySemanticsEnabledChanged];
1071 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1074 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1075 toTarget:(uint16_t)target
1076 withData:(fml::MallocMapping)data {
1077 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1084 #pragma mark - Private methods
1086 - (void)sendUserLocales {
1087 if (!
self.running) {
1092 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1093 std::vector<FlutterLocale> flutterLocales;
1094 flutterLocales.reserve(locales.count);
1095 for (NSString* localeID in [NSLocale preferredLanguages]) {
1096 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1097 [locales addObject:locale];
1101 std::vector<const FlutterLocale*> flutterLocaleList;
1102 flutterLocaleList.reserve(flutterLocales.size());
1103 std::transform(flutterLocales.begin(), flutterLocales.end(),
1104 std::back_inserter(flutterLocaleList),
1105 [](
const auto& arg) ->
const auto* { return &arg; });
1106 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1109 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1110 NSData* messageData = nil;
1111 if (message->message_size > 0) {
1112 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1113 length:message->message_size
1116 NSString* channel = @(message->channel);
1117 __block
const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1119 NSMutableArray* isResponseValid =
self.isResponseValid;
1120 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1121 _embedderAPI.SendPlatformMessageResponse;
1123 @
synchronized(isResponseValid) {
1124 if (![isResponseValid[0] boolValue]) {
1128 if (responseHandle) {
1129 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1130 static_cast<const uint8_t*
>(response.bytes), response.length);
1131 responseHandle = NULL;
1133 NSLog(
@"Error: Message responses can be sent only once. Ignoring duplicate response "
1142 handlerInfo.
handler(messageData, binaryResponseHandler);
1144 binaryResponseHandler(nil);
1148 - (void)engineCallbackOnPreEngineRestart {
1149 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1151 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1154 [_platformViewController reset];
1160 - (void)onVSync:(uintptr_t)baton {
1167 if ([NSThread isMainThread]) {
1177 - (void)shutDownEngine {
1178 if (_engine ==
nullptr) {
1182 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1183 if (result != kSuccess) {
1184 NSLog(
@"Could not de-initialize the Flutter engine: error %d", result);
1187 result = _embedderAPI.Shutdown(_engine);
1188 if (result != kSuccess) {
1189 NSLog(
@"Failed to shut down Flutter engine: error %d", result);
1195 NSAssert([[NSThread currentThread] isMainThread],
@"Must be called on the main thread.");
1196 return (__bridge
FlutterEngine*)
reinterpret_cast<void*
>(identifier);
1199 - (void)setUpPlatformViewChannel {
1206 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1207 [[weakSelf platformViewController] handleMethodCall:call result:result];
1211 - (void)setUpAccessibilityChannel {
1217 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1218 [weakSelf handleAccessibilityEvent:message];
1221 - (void)setUpNotificationCenterListeners {
1222 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1224 [center addObserver:self
1225 selector:@selector(onAccessibilityStatusChanged:)
1226 name:kEnhancedUserInterfaceNotification
1228 [center addObserver:self
1229 selector:@selector(applicationWillTerminate:)
1230 name:NSApplicationWillTerminateNotification
1232 [center addObserver:self
1233 selector:@selector(windowDidChangeScreen:)
1234 name:NSWindowDidChangeScreenNotification
1236 [center addObserver:self
1237 selector:@selector(updateDisplayConfig:)
1238 name:NSApplicationDidChangeScreenParametersNotification
1242 - (void)addInternalPlugins {
1255 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1256 [weakSelf handleMethodCall:call result:result];
1260 - (void)didUpdateMouseCursor:(NSCursor*)cursor {
1264 [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1267 - (void)applicationWillTerminate:(NSNotification*)notification {
1268 [
self shutDownEngine];
1271 - (void)windowDidChangeScreen:(NSNotification*)notification {
1274 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1276 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1277 [
self updateWindowMetricsForViewController:nextViewController];
1281 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1282 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1283 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1285 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1289 self.semanticsEnabled = enabled;
1291 - (void)handleAccessibilityEvent:(NSDictionary<NSString*,
id>*)annotatedEvent {
1292 NSString* type = annotatedEvent[@"type"];
1293 if ([type isEqualToString:
@"announce"]) {
1294 NSString* message = annotatedEvent[@"data"][@"message"];
1295 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1296 if (message == nil) {
1300 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1301 ? NSAccessibilityPriorityHigh
1302 : NSAccessibilityPriorityMedium;
1304 [
self announceAccessibilityMessage:message withPriority:priority];
1308 - (void)announceAccessibilityMessage:(NSString*)message
1309 withPriority:(NSAccessibilityPriorityLevel)priority {
1310 NSAccessibilityPostNotificationWithUserInfo(
1311 [
self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1312 NSAccessibilityAnnouncementRequestedNotification,
1313 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1316 if ([call.
method isEqualToString:
@"SystemNavigator.pop"]) {
1317 [[NSApplication sharedApplication] terminate:self];
1319 }
else if ([call.
method isEqualToString:
@"SystemSound.play"]) {
1320 [
self playSystemSound:call.arguments];
1322 }
else if ([call.
method isEqualToString:
@"Clipboard.getData"]) {
1323 result([
self getClipboardData:call.
arguments]);
1324 }
else if ([call.
method isEqualToString:
@"Clipboard.setData"]) {
1325 [
self setClipboardData:call.arguments];
1327 }
else if ([call.
method isEqualToString:
@"Clipboard.hasStrings"]) {
1328 result(@{
@"value" : @([
self clipboardHasStrings])});
1329 }
else if ([call.
method isEqualToString:
@"System.exitApplication"]) {
1330 if ([
self terminationHandler] == nil) {
1335 [NSApp terminate:self];
1338 [[
self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1340 }
else if ([call.
method isEqualToString:
@"System.initializationComplete"]) {
1341 if ([
self terminationHandler] != nil) {
1342 [
self terminationHandler].acceptingRequests = YES;
1350 - (void)playSystemSound:(NSString*)soundType {
1351 if ([soundType isEqualToString:
@"SystemSoundType.alert"]) {
1356 - (NSDictionary*)getClipboardData:(NSString*)format {
1358 NSString* stringInPasteboard = [
self.pasteboard stringForType:NSPasteboardTypeString];
1359 return stringInPasteboard == nil ? nil : @{
@"text" : stringInPasteboard};
1364 - (void)setClipboardData:(NSDictionary*)data {
1365 NSString* text = data[@"text"];
1366 [
self.pasteboard clearContents];
1367 if (text && ![text isEqual:[NSNull
null]]) {
1368 [
self.pasteboard setString:text forType:NSPasteboardTypeString];
1372 - (BOOL)clipboardHasStrings {
1373 return [
self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1376 - (std::vector<std::string>)switches {
1380 #pragma mark - FlutterAppLifecycleDelegate
1383 NSString* nextState =
1384 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1385 [
self sendOnChannel:kFlutterLifecycleChannel
1386 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1393 - (void)handleWillBecomeActive:(NSNotification*)notification {
1396 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1398 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1406 - (void)handleWillResignActive:(NSNotification*)notification {
1409 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1411 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1419 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1420 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1421 if (occlusionState & NSApplicationOcclusionStateVisible) {
1424 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1426 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1430 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1434 #pragma mark - FlutterBinaryMessenger
1436 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1437 [
self sendOnChannel:channel message:message binaryReply:nil];
1440 - (void)sendOnChannel:(NSString*)channel
1441 message:(NSData* _Nullable)message
1443 FlutterPlatformMessageResponseHandle* response_handle =
nullptr;
1448 auto captures = std::make_unique<Captures>();
1449 captures->reply = callback;
1450 auto message_reply = [](
const uint8_t* data,
size_t data_size,
void*
user_data) {
1451 auto captures =
reinterpret_cast<Captures*
>(
user_data);
1452 NSData* reply_data = nil;
1453 if (data !=
nullptr && data_size > 0) {
1454 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1456 captures->reply(reply_data);
1460 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1461 _engine, message_reply, captures.get(), &response_handle);
1462 if (create_result != kSuccess) {
1463 NSLog(
@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1469 FlutterPlatformMessage platformMessage = {
1470 .struct_size =
sizeof(FlutterPlatformMessage),
1471 .channel = [channel UTF8String],
1472 .message =
static_cast<const uint8_t*
>(message.bytes),
1473 .message_size = message.length,
1474 .response_handle = response_handle,
1477 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1478 if (message_result != kSuccess) {
1479 NSLog(
@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1483 if (response_handle !=
nullptr) {
1484 FlutterEngineResult release_result =
1485 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1486 if (release_result != kSuccess) {
1487 NSLog(
@"Failed to release the response handle (%d).", release_result);
1493 binaryMessageHandler:
1498 handler:[handler copy]];
1505 NSString* foundChannel = nil;
1508 if ([handlerInfo.
connection isEqual:@(connection)]) {
1514 [_messengerHandlers removeObjectForKey:foundChannel];
1518 #pragma mark - FlutterPluginRegistry
1521 id<FlutterPluginRegistrar> registrar =
self.pluginRegistrars[pluginName];
1525 self.pluginRegistrars[pluginName] = registrarImpl;
1526 registrar = registrarImpl;
1531 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1535 #pragma mark - FlutterTextureRegistrar
1538 return [_renderer registerTexture:texture];
1541 - (BOOL)registerTextureWithID:(int64_t)textureId {
1542 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1545 - (void)textureFrameAvailable:(int64_t)textureID {
1546 [_renderer textureFrameAvailable:textureID];
1549 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1550 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1553 - (void)unregisterTexture:(int64_t)textureID {
1554 [_renderer unregisterTexture:textureID];
1557 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1558 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1561 #pragma mark - Task runner integration
1563 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1566 const auto engine_time = _embedderAPI.GetCurrentTime();
1570 if (self != nil && self->_engine != nil) {
1571 auto result = _embedderAPI.RunTask(self->_engine, &task);
1572 if (result != kSuccess) {
1573 NSLog(@"Could not post a task to the Flutter engine.");
1577 afterDelay:(targetTime - (double)engine_time) / NSEC_PER_SEC];
1581 - (
flutter::FlutterCompositor*)macOSCompositor {
1585 #pragma mark - FlutterKeyboardManagerDelegate
1590 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
1591 callback:(FlutterKeyEventCallback)callback
1592 userData:(
void*)userData {
1593 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
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
FlutterMethodChannel * _platformViewsChannel
_FlutterEngineAOTData * _aotData
std::unique_ptr< flutter::FlutterCompositor > _macOSCompositor
static const int kMainThreadPriority
static void OnPlatformMessage(const FlutterPlatformMessage *message, void *user_data)
FlutterPlatformViewController * _platformViewController
FlutterBasicMessageChannel * _accessibilityChannel
FlutterBasicMessageChannel * _settingsChannel
static FlutterLocale FlutterLocaleFromNSLocale(NSLocale *locale)
BOOL _allowHeadlessExecution
NSMutableDictionary< NSString *, FlutterEngineHandlerInfo * > * _messengerHandlers
FlutterBinaryMessengerConnection _currentMessengerConnection
FlutterMethodChannel * _platformChannel
NSString *const kFlutterLifecycleChannel
static NSString *const kEnhancedUserInterfaceNotification
The private notification for voice over.
NSMapTable< NSNumber *, FlutterVSyncWaiter * > * _vsyncWaiters
NSString *const kFlutterPlatformChannel
FlutterTextInputPlugin * _textInputPlugin
FlutterDartProject * _project
NSMapTable * _viewControllers
FlutterCompositor _compositor
__weak FlutterView * _lastViewWithPointerEvent
FlutterKeyboardManager * _keyboardManager
FlutterTerminationCallback _terminator
constexpr char kTextPlainFormat[]
Clipboard plain text format.
__weak FlutterEngine * _flutterEngine
FlutterBinaryMessengerRelay * _binaryMessenger
static NSString *const kEnhancedUserInterfaceKey
NSString *const kFlutterSettingsChannel
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)
int64_t FlutterViewIdentifier
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
NSString * lookupKeyForAsset:(NSString *asset)
NSInteger clearContents()
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
instancetype displayLinkWithView:(NSView *view)
FlutterBinaryMessageHandler handler
id< FlutterBinaryMessenger > binaryMessenger
NSObject * publishedValue
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)
void performBlock:afterDelay:(void(^ block)(void),[afterDelay] NSTimeInterval delay)
void performBlock:(void(^ block)(void))
void ensureMainLoopInitialized()
Converts between the time representation used by Flutter Engine and CAMediaTime.
uint64_t CAMediaTimeToEngineTime:(CFTimeInterval time)
void waitForVSync:(uintptr_t baton)
void onPreEngineRestart()
FlutterViewIdentifier viewIdentifier
void onAccessibilityStatusChanged:(BOOL enabled)
std::vector< std::string > GetSwitchesFromEnvironment()
instancetype sharedInstance()
void handleMethodCall:result:(FlutterMethodCall *call,[result] FlutterResult result)