12 #include "flutter/common/constants.h"
15 #include "flutter/shell/platform/embedder/embedder.h"
38 using flutter::kFlutterImplicitViewId;
45 FlutterLocale flutterLocale = {};
46 flutterLocale.struct_size =
sizeof(FlutterLocale);
47 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
48 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
49 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
50 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
56 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
68 - (instancetype)initWithConnection:(NSNumber*)connection
77 - (instancetype)initWithConnection:(NSNumber*)connection
80 NSAssert(
self,
@"Super init cannot be nil");
101 @property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
106 @property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
111 @property(nonatomic, readonly)
112 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
139 - (void)shutDownIfNeeded;
144 - (void)sendUserLocales;
149 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
157 - (void)engineCallbackOnPreEngineRestart;
163 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
169 - (void)loadAOTData:(NSString*)assetsDir;
174 - (void)setUpPlatformViewChannel;
179 - (void)setUpAccessibilityChannel;
198 _acceptingRequests = NO;
200 _terminator = terminator ? terminator : ^(
id sender) {
203 [[NSApplication sharedApplication] terminate:sender];
205 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
206 if ([appDelegate respondsToSelector:
@selector(setTerminationHandler:)]) {
208 flutterAppDelegate.terminationHandler =
self;
215 - (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*,
id>*)arguments
217 NSString* type = arguments[@"type"];
223 FlutterAppExitType exitType =
224 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
233 - (void)requestApplicationTermination:(
id)sender
234 exitType:(FlutterAppExitType)type
236 _shouldTerminate = YES;
237 if (![
self acceptingRequests]) {
240 type = kFlutterAppExitTypeRequired;
243 case kFlutterAppExitTypeCancelable: {
247 [_engine sendOnChannel:kFlutterPlatformChannel
248 message:[codec encodeMethodCall:methodCall]
249 binaryReply:^(NSData* _Nullable reply) {
250 NSAssert(_terminator, @"terminator shouldn't be nil");
251 id decoded_reply = [codec decodeEnvelope:reply];
252 if ([decoded_reply isKindOfClass:[
FlutterError class]]) {
254 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
259 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
260 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
265 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
266 if ([replyArgs[@"response"] isEqual:@"exit"]) {
268 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
269 _shouldTerminate = NO;
277 case kFlutterAppExitTypeRequired:
278 NSAssert(
_terminator,
@"terminator shouldn't be nil");
291 return [[NSPasteboard generalPasteboard] clearContents];
294 - (NSString*)stringForType:(NSPasteboardType)dataType {
295 return [[NSPasteboard generalPasteboard] stringForType:dataType];
298 - (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
299 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
310 - (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
324 NSString* _pluginKey;
330 - (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
333 _pluginKey = [pluginKey copy];
335 _publishedValue = [NSNull null];
340 #pragma mark - FlutterPluginRegistrar
351 return [
self viewForIdentifier:kFlutterImplicitViewId];
356 if (controller == nil) {
359 if (!controller.viewLoaded) {
360 [controller loadView];
362 return controller.flutterView;
365 - (void)addMethodCallDelegate:(nonnull
id<
FlutterPlugin>)delegate
373 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
375 id<FlutterAppLifecycleProvider> lifeCycleProvider =
376 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
377 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
378 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
383 withId:(nonnull NSString*)factoryId {
384 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
387 - (void)publish:(NSObject*)value {
388 _publishedValue = value;
391 - (NSString*)lookupKeyForAsset:(NSString*)asset {
395 - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
402 #pragma mark - Static methods provided to engine configuration
406 [engine engineCallbackOnPlatformMessage:message];
483 - (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
484 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
489 static void SetThreadPriority(FlutterThreadPriority priority) {
490 if (priority == kDisplay || priority == kRaster) {
491 pthread_t thread = pthread_self();
494 if (!pthread_getschedparam(thread, &policy, ¶m)) {
496 pthread_setschedparam(thread, policy, ¶m);
498 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
502 - (instancetype)initWithName:(NSString*)labelPrefix
504 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
506 NSAssert(
self,
@"Super init cannot be nil");
513 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
514 _pluginRegistrars = [[NSMutableDictionary alloc] init];
517 _semanticsEnabled = NO;
519 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
520 [_isResponseValid addObject:@YES];
523 _embedderAPI.struct_size =
sizeof(FlutterEngineProcTable);
524 FlutterEngineGetProcAddresses(&_embedderAPI);
529 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
530 [notificationCenter addObserver:self
531 selector:@selector(sendUserLocales)
532 name:NSCurrentLocaleDidChangeNotification
543 [
self setUpPlatformViewChannel];
544 [
self setUpAccessibilityChannel];
545 [
self setUpNotificationCenterListeners];
546 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
550 id<FlutterAppLifecycleProvider> lifecycleProvider =
551 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
552 [lifecycleProvider addApplicationLifecycleDelegate:self];
554 _terminationHandler = nil;
563 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
565 id<FlutterAppLifecycleProvider> lifecycleProvider =
566 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
567 [lifecycleProvider removeApplicationLifecycleDelegate:self];
572 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
574 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
580 for (NSString* pluginName in _pluginRegistrars) {
581 [_pluginRegistrars[pluginName] publish:[NSNull null]];
583 @
synchronized(_isResponseValid) {
584 [_isResponseValid removeAllObjects];
585 [_isResponseValid addObject:@NO];
587 [
self shutDownEngine];
589 _embedderAPI.CollectAOTData(
_aotData);
593 - (BOOL)runWithEntrypoint:(NSString*)entrypoint {
599 NSLog(
@"Attempted to run an engine with no view controller without headless mode enabled.");
603 [
self addInternalPlugins];
606 std::vector<const char*> argv = {[
self.executableName UTF8String]};
607 std::vector<std::string> switches =
self.switches;
611 std::find(switches.begin(), switches.end(),
"--enable-impeller=true") != switches.end()) {
612 switches.push_back(
"--enable-impeller=true");
615 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
616 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
618 std::vector<const char*> dartEntrypointArgs;
619 for (NSString* argument in [
_project dartEntrypointArguments]) {
620 dartEntrypointArgs.push_back([argument UTF8String]);
623 FlutterProjectArgs flutterArguments = {};
624 flutterArguments.struct_size =
sizeof(FlutterProjectArgs);
625 flutterArguments.assets_path =
_project.assetsPath.UTF8String;
626 flutterArguments.icu_data_path =
_project.ICUDataPath.UTF8String;
627 flutterArguments.command_line_argc =
static_cast<int>(argv.size());
628 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
629 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)
OnPlatformMessage;
630 flutterArguments.update_semantics_callback2 = [](
const FlutterSemanticsUpdate2* update,
636 [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
638 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
639 flutterArguments.shutdown_dart_vm_when_done =
true;
640 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
641 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
642 flutterArguments.root_isolate_create_callback =
_project.rootIsolateCreateCallback;
643 flutterArguments.log_message_callback = [](
const char* tag,
const char* message,
646 std::cout << tag <<
": ";
648 std::cout << message << std::endl;
651 flutterArguments.engine_id =
reinterpret_cast<int64_t
>((__bridge
void*)
self);
653 static size_t sTaskRunnerIdentifiers = 0;
654 const FlutterTaskRunnerDescription cocoa_task_runner_description = {
655 .struct_size =
sizeof(FlutterTaskRunnerDescription),
657 .
user_data = (__bridge_retained
void*)
self,
658 .runs_task_on_current_thread_callback = [](
void*
user_data) ->
bool {
659 return [[NSThread currentThread] isMainThread];
661 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
664 [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
666 .identifier = ++sTaskRunnerIdentifiers,
667 .destruction_callback =
674 const FlutterCustomTaskRunners custom_task_runners = {
675 .struct_size =
sizeof(FlutterCustomTaskRunners),
676 .platform_task_runner = &cocoa_task_runner_description,
677 .thread_priority_setter = SetThreadPriority};
678 flutterArguments.custom_task_runners = &custom_task_runners;
680 [
self loadAOTData:_project.assetsPath];
682 flutterArguments.aot_data =
_aotData;
685 flutterArguments.compositor = [
self createFlutterCompositor];
687 flutterArguments.on_pre_engine_restart_callback = [](
void*
user_data) {
689 [engine engineCallbackOnPreEngineRestart];
692 flutterArguments.vsync_callback = [](
void*
user_data, intptr_t baton) {
694 [engine onVSync:baton];
697 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
698 FlutterEngineResult result = _embedderAPI.Initialize(
699 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge
void*)(
self), &_engine);
700 if (result != kSuccess) {
701 NSLog(
@"Failed to initialize Flutter engine: error %d", result);
705 result = _embedderAPI.RunInitialized(_engine);
706 if (result != kSuccess) {
707 NSLog(
@"Failed to run an initialized engine: error %d", result);
711 [
self sendUserLocales];
714 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
716 while ((nextViewController = [viewControllerEnumerator nextObject])) {
717 [
self updateWindowMetricsForViewController:nextViewController];
720 [
self updateDisplayConfig];
723 [
self sendInitialSettings];
727 - (void)loadAOTData:(NSString*)assetsDir {
728 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
732 BOOL isDirOut =
false;
733 NSFileManager* fileManager = [NSFileManager defaultManager];
737 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
739 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
743 FlutterEngineAOTDataSource source = {};
744 source.type = kFlutterEngineAOTDataSourceTypeElfPath;
745 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
747 auto result = _embedderAPI.CreateAOTData(&source, &
_aotData);
748 if (result != kSuccess) {
749 NSLog(
@"Failed to load AOT data from: %@", elfPath);
756 NSAssert(controller != nil,
@"The controller must not be nil.");
757 NSAssert(controller.
engine == nil,
758 @"The FlutterViewController is unexpectedly attached to "
759 @"engine %@ before initialization.",
762 @"The requested view ID is occupied.");
763 [_viewControllers setObject:controller forKey:@(viewIdentifier)];
764 [controller setUpWithEngine:self
765 viewIdentifier:viewIdentifier
766 threadSynchronizer:_threadSynchronizer];
767 NSAssert(controller.
viewIdentifier == viewIdentifier,
@"Failed to assign view ID.");
771 NSAssert(controller.
attached,
@"The FlutterViewController should switch to the attached mode "
772 @"after it is added to a FlutterEngine.");
773 NSAssert(controller.
engine ==
self,
774 @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
777 if (controller.viewLoaded) {
778 [
self viewControllerViewDidLoad:controller];
787 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
790 uint64_t targetTimeNanos =
792 FlutterEngine* engine = weakSelf;
798 [engine->_threadSynchronizer performOnPlatformThread:^{
799 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
805 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
814 if (controller != nil) {
815 [controller detachFromEngine];
817 @"The FlutterViewController unexpectedly stays attached after being removed. "
818 @"In unit tests, this is likely because either the FlutterViewController or "
819 @"the FlutterEngine is mocked. Please subclass these classes instead.");
821 [_viewControllers removeObjectForKey:@(viewIdentifier)];
823 [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
827 - (void)shutDownIfNeeded {
829 [
self shutDownEngine];
835 NSAssert(controller == nil || controller.
viewIdentifier == viewIdentifier,
836 @"The stored controller has unexpected view ID.");
842 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
843 if (currentController == controller) {
847 if (currentController == nil && controller != nil) {
849 NSAssert(controller.
engine == nil,
850 @"Failed to set view controller to the engine: "
851 @"The given FlutterViewController is already attached to an engine %@. "
852 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
853 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
855 [
self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
856 }
else if (currentController != nil && controller == nil) {
857 NSAssert(currentController.
viewIdentifier == kFlutterImplicitViewId,
858 @"The default controller has an unexpected ID %llu", currentController.
viewIdentifier);
860 [
self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
861 [
self shutDownIfNeeded];
865 @"Failed to set view controller to the engine: "
866 @"The engine already has an implicit view controller %@. "
867 @"If you wanted to make the implicit view render in a different window, "
868 @"you should attach the current view controller to the window instead.",
874 return [
self viewControllerForIdentifier:kFlutterImplicitViewId];
877 - (FlutterCompositor*)createFlutterCompositor {
879 _compositor.struct_size =
sizeof(FlutterCompositor);
882 _compositor.create_backing_store_callback = [](
const FlutterBackingStoreConfig* config,
883 FlutterBackingStore* backing_store_out,
887 config, backing_store_out);
890 _compositor.collect_backing_store_callback = [](
const FlutterBackingStore* backing_store,
894 _compositor.present_view_callback = [](
const FlutterPresentViewInfo* info) {
896 ->Present(info->view_id, info->layers, info->layers_count);
908 #pragma mark - Framework-internal methods
913 NSAssert(
self.viewController == nil,
914 @"The engine already has a view controller for the implicit view.");
915 self.viewController = controller;
919 [
self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
920 [
self shutDownIfNeeded];
924 return _engine !=
nullptr;
927 - (void)updateDisplayConfig:(NSNotification*)notification {
928 [
self updateDisplayConfig];
931 - (NSArray<NSScreen*>*)screens {
932 return [NSScreen screens];
935 - (void)updateDisplayConfig {
940 std::vector<FlutterEngineDisplay> displays;
941 for (NSScreen* screen : [
self screens]) {
942 CGDirectDisplayID displayID =
943 static_cast<CGDirectDisplayID
>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
945 double devicePixelRatio = screen.backingScaleFactor;
946 FlutterEngineDisplay display;
947 display.struct_size =
sizeof(display);
948 display.display_id = displayID;
949 display.single_display =
false;
950 display.width =
static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
951 display.height =
static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
952 display.device_pixel_ratio = devicePixelRatio;
954 CVDisplayLinkRef displayLinkRef = nil;
955 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
958 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
959 if (!(nominal.flags & kCVTimeIsIndefinite)) {
960 double refreshRate =
static_cast<double>(nominal.timeScale) / nominal.timeValue;
961 display.refresh_rate = round(refreshRate);
963 CVDisplayLinkRelease(displayLinkRef);
965 display.refresh_rate = 0;
968 displays.push_back(display);
970 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
971 displays.data(), displays.size());
974 - (void)onSettingsChanged:(NSNotification*)notification {
976 NSString* brightness =
977 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
978 [_settingsChannel sendMessage:@{
979 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
981 @"textScaleFactor" : @1.0,
986 - (void)sendInitialSettings {
988 [[NSDistributedNotificationCenter defaultCenter]
990 selector:@selector(onSettingsChanged:)
991 name:@"AppleInterfaceThemeChangedNotification"
993 [
self onSettingsChanged:nil];
996 - (FlutterEngineProcTable&)embedderAPI {
1000 - (nonnull NSString*)executableName {
1001 return [[[NSProcessInfo processInfo] arguments] firstObject] ?:
@"Flutter";
1005 if (!_engine || !viewController || !viewController.viewLoaded) {
1008 NSAssert([
self viewControllerForIdentifier:viewController.
viewIdentifier] == viewController,
1009 @"The provided view controller is not attached to this engine.");
1010 NSView* view = viewController.flutterView;
1011 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1012 CGSize scaledSize = scaledBounds.size;
1013 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
1014 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1015 const FlutterWindowMetricsEvent windowMetricsEvent = {
1016 .struct_size =
sizeof(windowMetricsEvent),
1017 .width =
static_cast<size_t>(scaledSize.width),
1018 .height =
static_cast<size_t>(scaledSize.height),
1019 .pixel_ratio = pixelRatio,
1020 .left =
static_cast<size_t>(scaledBounds.origin.x),
1021 .top =
static_cast<size_t>(scaledBounds.origin.y),
1022 .display_id =
static_cast<uint64_t
>(displayId),
1025 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1028 - (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1029 _embedderAPI.SendPointerEvent(_engine, &event, 1);
1033 - (void)setSemanticsEnabled:(BOOL)enabled {
1034 if (_semanticsEnabled == enabled) {
1037 _semanticsEnabled = enabled;
1040 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1042 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1043 [nextViewController notifySemanticsEnabledChanged];
1046 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1049 - (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1050 toTarget:(uint16_t)target
1051 withData:(fml::MallocMapping)data {
1052 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1059 #pragma mark - Private methods
1061 - (void)sendUserLocales {
1062 if (!
self.running) {
1067 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1068 std::vector<FlutterLocale> flutterLocales;
1069 flutterLocales.reserve(locales.count);
1070 for (NSString* localeID in [NSLocale preferredLanguages]) {
1071 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1072 [locales addObject:locale];
1076 std::vector<const FlutterLocale*> flutterLocaleList;
1077 flutterLocaleList.reserve(flutterLocales.size());
1078 std::transform(flutterLocales.begin(), flutterLocales.end(),
1079 std::back_inserter(flutterLocaleList),
1080 [](
const auto& arg) ->
const auto* { return &arg; });
1081 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1084 - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1085 NSData* messageData = nil;
1086 if (message->message_size > 0) {
1087 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1088 length:message->message_size
1091 NSString* channel = @(message->channel);
1092 __block
const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1094 NSMutableArray* isResponseValid =
self.isResponseValid;
1095 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1096 _embedderAPI.SendPlatformMessageResponse;
1098 @
synchronized(isResponseValid) {
1099 if (![isResponseValid[0] boolValue]) {
1103 if (responseHandle) {
1104 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1105 static_cast<const uint8_t*
>(response.bytes), response.length);
1106 responseHandle = NULL;
1108 NSLog(
@"Error: Message responses can be sent only once. Ignoring duplicate response "
1117 handlerInfo.
handler(messageData, binaryResponseHandler);
1119 binaryResponseHandler(nil);
1123 - (void)engineCallbackOnPreEngineRestart {
1124 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1126 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1129 [_platformViewController reset];
1133 - (void)onVSync:(uintptr_t)baton {
1145 - (void)shutDownEngine {
1146 if (_engine ==
nullptr) {
1150 [_threadSynchronizer shutdown];
1153 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1154 if (result != kSuccess) {
1155 NSLog(
@"Could not de-initialize the Flutter engine: error %d", result);
1158 result = _embedderAPI.Shutdown(_engine);
1159 if (result != kSuccess) {
1160 NSLog(
@"Failed to shut down Flutter engine: error %d", result);
1166 NSAssert([[NSThread currentThread] isMainThread],
@"Must be called on the main thread.");
1167 return (__bridge
FlutterEngine*)
reinterpret_cast<void*
>(identifier);
1170 - (void)setUpPlatformViewChannel {
1177 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1178 [[weakSelf platformViewController] handleMethodCall:call result:result];
1182 - (void)setUpAccessibilityChannel {
1188 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1189 [weakSelf handleAccessibilityEvent:message];
1192 - (void)setUpNotificationCenterListeners {
1193 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1195 [center addObserver:self
1196 selector:@selector(onAccessibilityStatusChanged:)
1197 name:kEnhancedUserInterfaceNotification
1199 [center addObserver:self
1200 selector:@selector(applicationWillTerminate:)
1201 name:NSApplicationWillTerminateNotification
1203 [center addObserver:self
1204 selector:@selector(windowDidChangeScreen:)
1205 name:NSWindowDidChangeScreenNotification
1207 [center addObserver:self
1208 selector:@selector(updateDisplayConfig:)
1209 name:NSApplicationDidChangeScreenParametersNotification
1213 - (void)addInternalPlugins {
1226 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1227 [weakSelf handleMethodCall:call result:result];
1231 - (void)didUpdateMouseCursor:(NSCursor*)cursor {
1235 [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1238 - (void)applicationWillTerminate:(NSNotification*)notification {
1239 [
self shutDownEngine];
1242 - (void)windowDidChangeScreen:(NSNotification*)notification {
1245 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1247 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1248 [
self updateWindowMetricsForViewController:nextViewController];
1252 - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1253 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1254 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1256 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1260 self.semanticsEnabled = enabled;
1262 - (void)handleAccessibilityEvent:(NSDictionary<NSString*,
id>*)annotatedEvent {
1263 NSString* type = annotatedEvent[@"type"];
1264 if ([type isEqualToString:
@"announce"]) {
1265 NSString* message = annotatedEvent[@"data"][@"message"];
1266 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1267 if (message == nil) {
1271 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1272 ? NSAccessibilityPriorityHigh
1273 : NSAccessibilityPriorityMedium;
1275 [
self announceAccessibilityMessage:message withPriority:priority];
1279 - (void)announceAccessibilityMessage:(NSString*)message
1280 withPriority:(NSAccessibilityPriorityLevel)priority {
1281 NSAccessibilityPostNotificationWithUserInfo(
1282 [
self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1283 NSAccessibilityAnnouncementRequestedNotification,
1284 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1287 if ([call.
method isEqualToString:
@"SystemNavigator.pop"]) {
1288 [[NSApplication sharedApplication] terminate:self];
1290 }
else if ([call.
method isEqualToString:
@"SystemSound.play"]) {
1291 [
self playSystemSound:call.arguments];
1293 }
else if ([call.
method isEqualToString:
@"Clipboard.getData"]) {
1294 result([
self getClipboardData:call.
arguments]);
1295 }
else if ([call.
method isEqualToString:
@"Clipboard.setData"]) {
1296 [
self setClipboardData:call.arguments];
1298 }
else if ([call.
method isEqualToString:
@"Clipboard.hasStrings"]) {
1299 result(@{
@"value" : @([
self clipboardHasStrings])});
1300 }
else if ([call.
method isEqualToString:
@"System.exitApplication"]) {
1301 if ([
self terminationHandler] == nil) {
1306 [NSApp terminate:self];
1309 [[
self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1311 }
else if ([call.
method isEqualToString:
@"System.initializationComplete"]) {
1312 if ([
self terminationHandler] != nil) {
1313 [
self terminationHandler].acceptingRequests = YES;
1321 - (void)playSystemSound:(NSString*)soundType {
1322 if ([soundType isEqualToString:
@"SystemSoundType.alert"]) {
1327 - (NSDictionary*)getClipboardData:(NSString*)format {
1329 NSString* stringInPasteboard = [
self.pasteboard stringForType:NSPasteboardTypeString];
1330 return stringInPasteboard == nil ? nil : @{
@"text" : stringInPasteboard};
1335 - (void)setClipboardData:(NSDictionary*)data {
1336 NSString* text = data[@"text"];
1337 [
self.pasteboard clearContents];
1338 if (text && ![text isEqual:[NSNull
null]]) {
1339 [
self.pasteboard setString:text forType:NSPasteboardTypeString];
1343 - (BOOL)clipboardHasStrings {
1344 return [
self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1347 - (std::vector<std::string>)switches {
1355 #pragma mark - FlutterAppLifecycleDelegate
1358 NSString* nextState =
1359 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1360 [
self sendOnChannel:kFlutterLifecycleChannel
1361 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1368 - (void)handleWillBecomeActive:(NSNotification*)notification {
1371 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1373 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1381 - (void)handleWillResignActive:(NSNotification*)notification {
1384 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1386 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1394 - (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1395 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1396 if (occlusionState & NSApplicationOcclusionStateVisible) {
1399 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1401 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1405 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1409 #pragma mark - FlutterBinaryMessenger
1411 - (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1412 [
self sendOnChannel:channel message:message binaryReply:nil];
1415 - (void)sendOnChannel:(NSString*)channel
1416 message:(NSData* _Nullable)message
1418 FlutterPlatformMessageResponseHandle* response_handle =
nullptr;
1423 auto captures = std::make_unique<Captures>();
1424 captures->reply = callback;
1425 auto message_reply = [](
const uint8_t* data,
size_t data_size,
void*
user_data) {
1426 auto captures =
reinterpret_cast<Captures*
>(
user_data);
1427 NSData* reply_data = nil;
1428 if (data !=
nullptr && data_size > 0) {
1429 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1431 captures->reply(reply_data);
1435 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1436 _engine, message_reply, captures.get(), &response_handle);
1437 if (create_result != kSuccess) {
1438 NSLog(
@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1444 FlutterPlatformMessage platformMessage = {
1445 .struct_size =
sizeof(FlutterPlatformMessage),
1446 .channel = [channel UTF8String],
1447 .message =
static_cast<const uint8_t*
>(message.bytes),
1448 .message_size = message.length,
1449 .response_handle = response_handle,
1452 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1453 if (message_result != kSuccess) {
1454 NSLog(
@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1458 if (response_handle !=
nullptr) {
1459 FlutterEngineResult release_result =
1460 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1461 if (release_result != kSuccess) {
1462 NSLog(
@"Failed to release the response handle (%d).", release_result);
1468 binaryMessageHandler:
1473 handler:[handler copy]];
1480 NSString* foundChannel = nil;
1483 if ([handlerInfo.
connection isEqual:@(connection)]) {
1489 [_messengerHandlers removeObjectForKey:foundChannel];
1493 #pragma mark - FlutterPluginRegistry
1496 id<FlutterPluginRegistrar> registrar =
self.pluginRegistrars[pluginName];
1500 self.pluginRegistrars[pluginName] = registrarImpl;
1501 registrar = registrarImpl;
1506 - (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1510 #pragma mark - FlutterTextureRegistrar
1513 return [_renderer registerTexture:texture];
1516 - (BOOL)registerTextureWithID:(int64_t)textureId {
1517 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1520 - (void)textureFrameAvailable:(int64_t)textureID {
1521 [_renderer textureFrameAvailable:textureID];
1524 - (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1525 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1528 - (void)unregisterTexture:(int64_t)textureID {
1529 [_renderer unregisterTexture:textureID];
1532 - (BOOL)unregisterTextureWithID:(int64_t)textureID {
1533 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1536 #pragma mark - Task runner integration
1538 - (void)runTaskOnEmbedder:(FlutterTask)task {
1540 auto result = _embedderAPI.RunTask(_engine, &task);
1541 if (result != kSuccess) {
1542 NSLog(
@"Could not post a task to the Flutter engine.");
1547 - (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1550 [weakSelf runTaskOnEmbedder:task];
1553 const auto engine_time = _embedderAPI.GetCurrentTime();
1554 if (targetTime <= engine_time) {
1555 dispatch_async(dispatch_get_main_queue(), worker);
1558 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, targetTime - engine_time),
1559 dispatch_get_main_queue(), worker);
1564 - (
flutter::FlutterCompositor*)macOSCompositor {
1568 #pragma mark - FlutterKeyboardManagerDelegate
1573 - (void)sendKeyEvent:(const FlutterKeyEvent&)event
1574 callback:(FlutterKeyEventCallback)callback
1575 userData:(
void*)userData {
1576 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);