19#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
21#import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
37#import <CoreVideo/CoreVideo.h>
38#import <IOSurface/IOSurface.h>
55 flutterLocale.
language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
56 flutterLocale.
country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
57 flutterLocale.
script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
58 flutterLocale.
variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
64 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
76- (instancetype)initWithConnection:(NSNumber*)connection
85- (instancetype)initWithConnection:(NSNumber*)connection
88 NSAssert(
self,
@"Super init cannot be nil");
101 FlutterMouseCursorPluginDelegate,
102 FlutterKeyboardManagerDelegate,
103 FlutterTextInputPluginDelegate>
110@property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
115@property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
120@property(nonatomic, readonly)
121 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
148- (void)shutDownIfNeeded;
153- (void)sendUserLocales;
164- (void)postMainThreadTask:(
FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
170- (void)loadAOTData:(NSString*)assetsDir;
175- (void)setUpPlatformViewChannel;
180- (void)setUpAccessibilityChannel;
199 _acceptingRequests = NO;
201 _terminator = terminator ? terminator : ^(
id sender) {
204 [[NSApplication sharedApplication] terminate:sender];
206 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
207 if ([appDelegate respondsToSelector:@selector(setTerminationHandler:)]) {
209 flutterAppDelegate.terminationHandler =
self;
216- (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*,
id>*)arguments
218 NSString*
type = arguments[@"type"];
224 FlutterAppExitType exitType =
225 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
227 [
self requestApplicationTermination:[NSApplication sharedApplication]
234- (void)requestApplicationTermination:(
id)sender
235 exitType:(FlutterAppExitType)type
237 _shouldTerminate = YES;
238 if (![
self acceptingRequests]) {
241 type = kFlutterAppExitTypeRequired;
244 case kFlutterAppExitTypeCancelable: {
248 [_engine sendOnChannel:kFlutterPlatformChannel
249 message:[codec encodeMethodCall:methodCall]
250 binaryReply:^(NSData* _Nullable reply) {
251 NSAssert(_terminator, @"terminator shouldn't be nil");
252 id decoded_reply = [codec decodeEnvelope:reply];
253 if ([decoded_reply isKindOfClass:[
FlutterError class]]) {
255 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
260 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
261 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
266 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
267 if ([replyArgs[@"response"] isEqual:@"exit"]) {
269 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
270 _shouldTerminate = NO;
278 case kFlutterAppExitTypeRequired:
279 NSAssert(
_terminator,
@"terminator shouldn't be nil");
292 return [[NSPasteboard generalPasteboard] clearContents];
295- (NSString*)stringForType:(NSPasteboardType)dataType {
296 return [[NSPasteboard generalPasteboard] stringForType:dataType];
299- (
BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
300 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
311- (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
325 NSString* _pluginKey;
331- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(
FlutterEngine*)flutterEngine {
334 _pluginKey = [pluginKey copy];
336 _publishedValue = [NSNull null];
341#pragma mark - FlutterPluginRegistrar
343- (
id<FlutterBinaryMessenger>)messenger {
347- (
id<FlutterTextureRegistry>)textures {
352 return [
self viewForIdentifier:kFlutterImplicitViewId];
357 if (controller == nil) {
360 if (!controller.viewLoaded) {
361 [controller loadView];
363 return controller.flutterView;
367 return [_flutterEngine viewControllerForIdentifier:kFlutterImplicitViewId];
370- (void)addMethodCallDelegate:(nonnull
id<FlutterPlugin>)delegate
373 [delegate handleMethodCall:call result:result];
377- (void)addApplicationDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
378 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
379 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
380 id<FlutterAppLifecycleProvider> lifeCycleProvider =
381 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
382 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
383 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
388 withId:(nonnull NSString*)factoryId {
389 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
392- (void)publish:(NSObject*)value {
393 _publishedValue =
value;
396- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
397 return [_flutterEngine valuePublishedByPlugin:pluginKey];
400- (NSString*)lookupKeyForAsset:(NSString*)asset {
404- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
411#pragma mark - Static methods provided to engine configuration
415 [engine engineCallbackOnPlatformMessage:message];
511- (instancetype)initWithName:(NSString*)labelPrefix project:(
FlutterDartProject*)project {
512 return [
self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
519 pthread_t thread = pthread_self();
522 if (!pthread_getschedparam(thread, &policy, ¶m)) {
524 pthread_setschedparam(thread, policy, ¶m);
526 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
530- (instancetype)initWithName:(NSString*)labelPrefix
532 allowHeadlessExecution:(
BOOL)allowHeadlessExecution {
534 NSAssert(
self,
@"Super init cannot be nil");
536 [FlutterRunLoop ensureMainLoopInitialized];
543 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
544 _pluginRegistrars = [[NSMutableDictionary alloc] init];
547 _semanticsEnabled = NO;
549 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
550 [_isResponseValid addObject:@YES];
562 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
563 [notificationCenter addObserver:self
564 selector:@selector(sendUserLocales)
565 name:NSCurrentLocaleDidChangeNotification
575 [
self setUpPlatformViewChannel];
580 [
self setUpAccessibilityChannel];
581 [
self setUpNotificationCenterListeners];
582 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
583 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
586 id<FlutterAppLifecycleProvider> lifecycleProvider =
587 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
588 [lifecycleProvider addApplicationLifecycleDelegate:self];
590 _terminationHandler = nil;
599 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
600 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
601 id<FlutterAppLifecycleProvider> lifecycleProvider =
602 static_cast<id<FlutterAppLifecycleProvider>
>(appDelegate);
603 [lifecycleProvider removeApplicationLifecycleDelegate:self];
608 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
610 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
616 for (NSString* pluginName in _pluginRegistrars) {
617 [_pluginRegistrars[pluginName] publish:[NSNull null]];
619 @
synchronized(_isResponseValid) {
620 [_isResponseValid removeAllObjects];
621 [_isResponseValid addObject:@NO];
623 [
self shutDownEngine];
625 _embedderAPI.CollectAOTData(
_aotData);
630 static size_t sTaskRunnerIdentifiers = 0;
635 .runs_task_on_current_thread_callback = [](
void*
user_data) ->
bool {
636 return [[NSThread currentThread] isMainThread];
638 .post_task_callback = [](
FlutterTask task, uint64_t target_time_nanos,
641 [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
643 .identifier = ++sTaskRunnerIdentifiers,
644 .destruction_callback =
651 return cocoa_task_runner_description;
656 if (controller == nil) {
660 [controller.flutterView.window makeFirstResponder:controller.flutterView];
664- (
BOOL)runWithEntrypoint:(NSString*)entrypoint {
670 NSLog(
@"Attempted to run an engine with no view controller without headless mode enabled.");
674 [
self addInternalPlugins];
677 std::vector<const char*>
argv = {[
self.executableName UTF8String]};
678 std::vector<std::string> switches =
self.switches;
681 if (std::find(switches.begin(), switches.end(),
"--enable-impeller=false") != switches.end()) {
684 "--enable-impeller=true") != switches.end()) {
685 switches.push_back(
"--enable-impeller=true");
688 if (std::find(switches.begin(), switches.end(),
"--enable-impeller=true") == switches.end()) {
689 FML_LOG(IMPORTANT) <<
"Using the Skia rendering backend (Metal).";
693 std::find(switches.begin(), switches.end(),
"--impeller-use-sdfs=true") != switches.end()) {
694 switches.push_back(
"--impeller-use-sdfs=true");
698 std::find(switches.begin(), switches.end(),
"--enable-flutter-gpu=true") != switches.end()) {
699 switches.push_back(
"--enable-flutter-gpu=true");
702 std::transform(switches.begin(), switches.end(), std::back_inserter(
argv),
703 [](
const std::string& arg) ->
const char* { return arg.c_str(); });
705 std::vector<const char*> dartEntrypointArgs;
706 for (NSString* argument in [
_project dartEntrypointArguments]) {
707 dartEntrypointArgs.push_back([argument UTF8String]);
723 [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
732 std::stringstream stream;
734 stream << tag <<
": ";
737 std::string log = stream.str();
738 [FlutterLogger logDirect:[NSString stringWithUTF8String:log.c_str()]];
741 flutterArguments.
engine_id =
reinterpret_cast<int64_t
>((__bridge
void*)
self);
743 if (std::find(switches.begin(), switches.end(),
"--enable-impeller=false") != switches.end()) {
744 enableWideGamut = NO;
748 BOOL mergedPlatformUIThread = YES;
749 NSNumber* enableMergedPlatformUIThread =
750 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
751 if (enableMergedPlatformUIThread != nil) {
752 mergedPlatformUIThread = enableMergedPlatformUIThread.boolValue;
755 if (!mergedPlatformUIThread) {
756 NSLog(
@"Warning: Merged threads is disabled. Running Flutter without merged threads is "
757 "deprecated and will be unsupported in a future release.\n"
759 "To turn on merged threads, update your macos/Runner/Info.plist file:\n"
761 " <key>FLTEnableMergedPlatformUIThread</key>\n"
764 "If you disabled merged threads to work around an issue, please report it here: "
765 "https://github.com/flutter/flutter/issues/150525.");
772 [
self createPlatformThreadTaskDescription];
773 std::optional<FlutterTaskRunnerDescription> uiTaskRunnerDescription;
774 if (mergedPlatformUIThread) {
775 uiTaskRunnerDescription = [
self createPlatformThreadTaskDescription];
780 .platform_task_runner = &platformTaskRunnerDescription,
781 .thread_priority_setter = SetThreadPriority,
782 .ui_task_runner = uiTaskRunnerDescription ? &uiTaskRunnerDescription.value() :
nullptr,
786 [
self loadAOTData:_project.assetsPath];
791 flutterArguments.
compositor = [
self createFlutterCompositor];
795 [engine engineCallbackOnPreEngineRestart];
800 [engine onVSync:baton];
806 [engine onFocusChangeRequest:request];
813 NSLog(
@"Failed to initialize Flutter engine: error %d", result);
817 result = _embedderAPI.RunInitialized(_engine);
819 NSLog(
@"Failed to run an initialized engine: error %d", result);
823 [
self sendUserLocales];
826 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
828 while ((nextViewController = [viewControllerEnumerator nextObject])) {
829 [
self updateWindowMetricsForViewController:nextViewController];
832 [
self updateDisplayConfig];
835 [
self sendInitialSettings];
839- (void)loadAOTData:(NSString*)assetsDir {
840 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
844 BOOL isDirOut =
false;
845 NSFileManager* fileManager = [NSFileManager defaultManager];
849 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
851 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
857 source.
elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
859 auto result = _embedderAPI.CreateAOTData(&source, &
_aotData);
861 NSLog(
@"Failed to load AOT data from: %@", elfPath);
868 NSAssert(controller != nil,
@"The controller must not be nil.");
870 NSAssert(controller.
engine == nil,
871 @"The FlutterViewController is unexpectedly attached to "
872 @"engine %@ before initialization.",
876 @"The requested view ID is occupied.");
877 [_viewControllers setObject:controller forKey:@(viewIdentifier)];
878 [controller setUpWithEngine:self viewIdentifier:viewIdentifier];
879 NSAssert(controller.
viewIdentifier == viewIdentifier,
@"Failed to assign view ID.");
883 NSAssert(controller.
attached,
@"The FlutterViewController should switch to the attached mode "
884 @"after it is added to a FlutterEngine.");
886 @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
889 if (controller.viewLoaded) {
890 [
self viewControllerViewDidLoad:controller];
893 if (viewIdentifier != kFlutterImplicitViewId) {
905 .view_metrics = &metrics,
908 auto added =
reinterpret_cast<bool*
>(r->
user_data);
912 _embedderAPI.AddView(_engine, &info);
915 NSLog(
@"Failed to add view with ID %llu", viewIdentifier);
925 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
928 uint64_t targetTimeNanos =
930 FlutterEngine* engine = weakSelf;
932 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
937 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
942 if (viewIdentifier != kFlutterImplicitViewId) {
943 bool removed =
false;
952 auto removed =
reinterpret_cast<bool*
>(r->user_data);
953 [FlutterRunLoop.mainRunLoop performBlock:^{
957 _embedderAPI.RemoveView(_engine, &info);
959 [[FlutterRunLoop mainRunLoop] pollFlutterMessagesOnce];
968 if (controller != nil) {
969 [controller detachFromEngine];
971 @"The FlutterViewController unexpectedly stays attached after being removed. "
972 @"In unit tests, this is likely because either the FlutterViewController or "
973 @"the FlutterEngine is mocked. Please subclass these classes instead.");
975 [_viewControllers removeObjectForKey:@(viewIdentifier)];
979 waiter = [_vsyncWaiters objectForKey:@(viewIdentifier)];
980 [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
985- (void)shutDownIfNeeded {
987 [
self shutDownEngine];
993 NSAssert(controller == nil || controller.
viewIdentifier == viewIdentifier,
994 @"The stored controller has unexpected view ID.");
1000 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
1001 if (currentController == controller) {
1005 if (currentController == nil && controller != nil) {
1007 NSAssert(controller.
engine == nil,
1008 @"Failed to set view controller to the engine: "
1009 @"The given FlutterViewController is already attached to an engine %@. "
1010 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
1011 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
1013 [
self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
1014 }
else if (currentController != nil && controller == nil) {
1015 NSAssert(currentController.
viewIdentifier == kFlutterImplicitViewId,
1016 @"The default controller has an unexpected ID %llu", currentController.
viewIdentifier);
1018 [
self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
1019 [
self shutDownIfNeeded];
1023 @"Failed to set view controller to the engine: "
1024 @"The engine already has an implicit view controller %@. "
1025 @"If you wanted to make the implicit view render in a different window, "
1026 @"you should attach the current view controller to the window instead.",
1032 return [
self viewControllerForIdentifier:kFlutterImplicitViewId];
1045 config, backing_store_out);
1054 ->Present(info->
view_id, info->layers, info->layers_count);
1062- (
id<FlutterBinaryMessenger>)binaryMessenger {
1066#pragma mark - Framework-internal methods
1072 NSAssert(
self.viewController == nil,
1073 @"The engine already has a view controller for the implicit view.");
1074 self.viewController = controller;
1079 [
self registerViewController:controller forIdentifier:viewIdentifier];
1083- (void)enableMultiView {
1085 NSAssert(
self.viewController == nil,
1086 @"Multiview can only be enabled before adding any view controllers.");
1098 _embedderAPI.SendViewFocusEvent(_engine, &event);
1108 _embedderAPI.SendViewFocusEvent(_engine, &event);
1112 [
self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
1113 [
self shutDownIfNeeded];
1117 return _engine !=
nullptr;
1120- (void)updateDisplayConfig:(NSNotification*)notification {
1121 [
self updateDisplayConfig];
1124- (NSArray<NSScreen*>*)screens {
1125 return [NSScreen screens];
1128- (void)updateDisplayConfig {
1133 std::vector<FlutterEngineDisplay>
displays;
1134 for (NSScreen* screen : [
self screens]) {
1135 CGDirectDisplayID displayID =
1136 static_cast<CGDirectDisplayID
>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
1138 double devicePixelRatio = screen.backingScaleFactor;
1143 display.
width =
static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
1144 display.
height =
static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
1147 CVDisplayLinkRef displayLinkRef = nil;
1148 CVReturn
error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
1151 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
1152 if (!(nominal.flags & kCVTimeIsIndefinite)) {
1153 double refreshRate =
static_cast<double>(nominal.timeScale) / nominal.timeValue;
1156 CVDisplayLinkRelease(displayLinkRef);
1167- (void)onSettingsChanged:(NSNotification*)notification {
1169 NSString* brightness =
1170 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
1171 [_settingsChannel sendMessage:@{
1172 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
1174 @"textScaleFactor" : @1.0,
1179- (void)sendInitialSettings {
1181 [[NSDistributedNotificationCenter defaultCenter]
1183 selector:@selector(onSettingsChanged:)
1184 name:@"AppleInterfaceThemeChangedNotification"
1186 [
self onSettingsChanged:nil];
1190 return _embedderAPI;
1193- (nonnull NSString*)executableName {
1194 return [[[NSProcessInfo processInfo] arguments] firstObject] ?:
@"Flutter";
1202 @"The provided view controller is not attached to this engine.");
1204 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1205 CGSize scaledSize = scaledBounds.size;
1206 double pixelRatio =
view.layer.contentsScale;
1207 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1210 .
width =
static_cast<size_t>(scaledSize.width),
1211 .height =
static_cast<size_t>(scaledSize.height),
1212 .pixel_ratio = pixelRatio,
1213 .left =
static_cast<size_t>(scaledBounds.origin.x),
1214 .top =
static_cast<size_t>(scaledBounds.origin.y),
1215 .display_id =
static_cast<uint64_t
>(displayId),
1218 if (
view.sizedToContents) {
1219 CGSize maximumContentSize = [view convertSizeToBacking:view.maximumContentSize];
1220 CGSize minimumContentSize = [view convertSizeToBacking:view.minimumContentSize];
1232 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1236 _embedderAPI.SendPointerEvent(_engine, &event, 1);
1240- (void)setSemanticsEnabled:(
BOOL)enabled {
1241 if (_semanticsEnabled == enabled) {
1244 _semanticsEnabled = enabled;
1247 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1249 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1250 [nextViewController notifySemanticsEnabledChanged];
1253 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1257 toTarget:(uint16_t)target
1258 withData:(
fml::MallocMapping)data {
1259 _embedderAPI.DispatchSemanticsAction(_engine,
target,
action,
data.GetMapping(),
data.GetSize());
1266#pragma mark - Private methods
1268- (void)sendUserLocales {
1269 if (!
self.running) {
1274 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1275 std::vector<FlutterLocale> flutterLocales;
1276 flutterLocales.reserve(locales.count);
1277 for (NSString* localeID in [NSLocale preferredLanguages]) {
1278 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1279 [locales addObject:locale];
1283 std::vector<const FlutterLocale*> flutterLocaleList;
1284 flutterLocaleList.reserve(flutterLocales.size());
1285 std::transform(flutterLocales.begin(), flutterLocales.end(),
1286 std::back_inserter(flutterLocaleList),
1287 [](
const auto& arg) ->
const auto* { return &arg; });
1288 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1292 NSData* messageData = nil;
1293 if (
message->message_size > 0) {
1294 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1295 length:message->message_size
1301 NSMutableArray* isResponseValid =
self.isResponseValid;
1303 _embedderAPI.SendPlatformMessageResponse;
1305 @
synchronized(isResponseValid) {
1306 if (![isResponseValid[0] boolValue]) {
1310 if (responseHandle) {
1311 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1312 static_cast<const uint8_t*
>(response.bytes), response.length);
1313 responseHandle = NULL;
1315 NSLog(
@"Error: Message responses can be sent only once. Ignoring duplicate response "
1324 handlerInfo.
handler(messageData, binaryResponseHandler);
1326 binaryResponseHandler(nil);
1330- (void)engineCallbackOnPreEngineRestart {
1331 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1333 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1336 [_windowController closeAllWindows];
1337 [_platformViewController reset];
1343- (void)onVSync:(uintptr_t)baton {
1348 [_vsyncWaiters objectForKey:[_vsyncWaiters.keyEnumerator nextObject]];
1349 if (waiter != nil) {
1355 self.embedderAPI.OnVsync(_engine, baton, 0, 0);
1358 if ([NSThread isMainThread]) {
1361 [FlutterRunLoop.mainRunLoop performBlock:block];
1368- (void)shutDownEngine {
1369 if (_engine ==
nullptr) {
1375 NSLog(
@"Could not de-initialize the Flutter engine: error %d", result);
1378 result = _embedderAPI.Shutdown(_engine);
1380 NSLog(
@"Failed to shut down Flutter engine: error %d", result);
1386 NSAssert([[NSThread currentThread] isMainThread],
@"Must be called on the main thread.");
1387 return (__bridge
FlutterEngine*)
reinterpret_cast<void*
>(identifier);
1390- (void)setUpPlatformViewChannel {
1397 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1398 [[weakSelf platformViewController] handleMethodCall:call result:result];
1402- (void)setUpAccessibilityChannel {
1408 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1409 [weakSelf handleAccessibilityEvent:message];
1412- (void)setUpNotificationCenterListeners {
1413 NSNotificationCenter*
center = [NSNotificationCenter defaultCenter];
1415 [center addObserver:self
1416 selector:@selector(onAccessibilityStatusChanged:)
1417 name:kEnhancedUserInterfaceNotification
1419 [center addObserver:self
1420 selector:@selector(applicationWillTerminate:)
1421 name:NSApplicationWillTerminateNotification
1423 [center addObserver:self
1424 selector:@selector(windowDidChangeScreen:)
1425 name:NSWindowDidChangeScreenNotification
1427 [center addObserver:self
1428 selector:@selector(updateDisplayConfig:)
1429 name:NSApplicationDidChangeScreenParametersNotification
1433- (void)addInternalPlugins {
1447 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1448 [weakSelf handleMethodCall:call result:result];
1455 [_screenshotChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1459 message:@"Engine deallocated."
1463 FlutterViewController* viewController =
1464 [strongSelf viewControllerForIdentifier:flutter::kFlutterImplicitViewId];
1465 if (!viewController) {
1467 message:@"No view controller."
1471 NSArray<FlutterSurface*>* frontSurfaces =
1472 viewController.flutterView.surfaceManager.frontSurfaces;
1473 if (frontSurfaces.count == 0) {
1480 FlutterSurface* surface = frontSurfaces.firstObject;
1481 IOSurfaceRef ioSurface = surface.ioSurface;
1483 size_t width = IOSurfaceGetWidth(ioSurface);
1484 size_t height = IOSurfaceGetHeight(ioSurface);
1485 size_t bytesPerRow = IOSurfaceGetBytesPerRow(ioSurface);
1486 size_t bytesPerElement = IOSurfaceGetBytesPerElement(ioSurface);
1487 uint32_t pixelFormat = (uint32_t)IOSurfaceGetPixelFormat(ioSurface);
1489 NSString* formatString;
1490 switch (pixelFormat) {
1491 case kCVPixelFormatType_40ARGBLEWideGamut:
1492 formatString = @"MTLPixelFormatBGRA10_XR";
1494 case kCVPixelFormatType_32BGRA:
1495 formatString = @"MTLPixelFormatBGRA8Unorm";
1498 formatString = [NSString stringWithFormat:@"Unknown(%u)", pixelFormat];
1502 IOSurfaceLock(ioSurface, kIOSurfaceLockReadOnly, nil);
1503 void* baseAddress = IOSurfaceGetBaseAddress(ioSurface);
1506 size_t packedBytesPerRow = width * bytesPerElement;
1507 NSMutableData* packedData = [NSMutableData dataWithLength:packedBytesPerRow * height];
1508 uint8_t* dest = (uint8_t*)packedData.mutableBytes;
1509 for (size_t row = 0; row < height; row++) {
1510 memcpy(dest + row * packedBytesPerRow, (uint8_t*)baseAddress + row * bytesPerRow,
1514 IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nil);
1525- (void)didUpdateMouseCursor:(NSCursor*)cursor {
1529 [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1532- (void)applicationWillTerminate:(NSNotification*)notification {
1533 [
self shutDownEngine];
1536- (void)windowDidChangeScreen:(NSNotification*)notification {
1539 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1541 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1542 [
self updateWindowMetricsForViewController:nextViewController];
1543 [nextViewController updateWideGamutForScreen];
1547- (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1548 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1549 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1551 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1555 self.semanticsEnabled = enabled;
1557- (void)handleAccessibilityEvent:(NSDictionary<NSString*,
id>*)annotatedEvent {
1558 NSString*
type = annotatedEvent[@"type"];
1559 if ([
type isEqualToString:
@"announce"]) {
1560 NSString*
message = annotatedEvent[@"data"][@"message"];
1561 NSNumber*
assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1566 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1567 ? NSAccessibilityPriorityHigh
1568 : NSAccessibilityPriorityMedium;
1570 [
self announceAccessibilityMessage:message withPriority:priority];
1574- (void)announceAccessibilityMessage:(NSString*)message
1575 withPriority:(NSAccessibilityPriorityLevel)priority {
1576 NSAccessibilityPostNotificationWithUserInfo(
1578 NSAccessibilityAnnouncementRequestedNotification,
1579 @{NSAccessibilityAnnouncementKey :
message, NSAccessibilityPriorityKey : @(priority)});
1582 if ([call.
method isEqualToString:
@"SystemNavigator.pop"]) {
1583 [[NSApplication sharedApplication] terminate:self];
1585 }
else if ([call.
method isEqualToString:
@"SystemSound.play"]) {
1586 [
self playSystemSound:call.arguments];
1588 }
else if ([call.
method isEqualToString:
@"Clipboard.getData"]) {
1589 result([
self getClipboardData:call.arguments]);
1590 }
else if ([call.
method isEqualToString:
@"Clipboard.setData"]) {
1591 [
self setClipboardData:call.arguments];
1593 }
else if ([call.
method isEqualToString:
@"Clipboard.hasStrings"]) {
1594 result(@{
@"value" : @([
self clipboardHasStrings])});
1595 }
else if ([call.
method isEqualToString:
@"System.exitApplication"]) {
1596 if ([
self terminationHandler] == nil) {
1601 [NSApp terminate:self];
1604 [[
self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1606 }
else if ([call.
method isEqualToString:
@"System.initializationComplete"]) {
1607 if ([
self terminationHandler] != nil) {
1608 [
self terminationHandler].acceptingRequests = YES;
1616- (void)playSystemSound:(NSString*)soundType {
1617 if ([soundType isEqualToString:
@"SystemSoundType.alert"]) {
1622- (NSDictionary*)getClipboardData:(NSString*)format {
1624 NSString* stringInPasteboard = [
self.pasteboard stringForType:NSPasteboardTypeString];
1625 return stringInPasteboard == nil ? nil : @{
@"text" : stringInPasteboard};
1630- (void)setClipboardData:(NSDictionary*)data {
1632 [
self.pasteboard clearContents];
1633 if (
text && ![
text isEqual:[NSNull null]]) {
1634 [
self.pasteboard setString:text forType:NSPasteboardTypeString];
1638- (
BOOL)clipboardHasStrings {
1639 return [
self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1642- (
std::vector<std::string>)switches {
1646#pragma mark - FlutterAppLifecycleDelegate
1649 NSString* nextState =
1650 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1651 [
self sendOnChannel:kFlutterLifecycleChannel
1652 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1659- (void)handleWillBecomeActive:(NSNotification*)notification {
1662 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1664 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1672- (void)handleWillResignActive:(NSNotification*)notification {
1675 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1677 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1685- (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1686 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1687 if (occlusionState & NSApplicationOcclusionStateVisible) {
1690 [
self setApplicationState:flutter::AppLifecycleState::kResumed];
1692 [
self setApplicationState:flutter::AppLifecycleState::kInactive];
1696 [
self setApplicationState:flutter::AppLifecycleState::kHidden];
1700#pragma mark - FlutterBinaryMessenger
1702- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1703 [
self sendOnChannel:channel message:message binaryReply:nil];
1706- (void)sendOnChannel:(NSString*)channel
1707 message:(NSData* _Nullable)message
1714 auto captures = std::make_unique<Captures>();
1716 auto message_reply = [](
const uint8_t*
data,
size_t data_size,
void*
user_data) {
1717 auto captures =
reinterpret_cast<Captures*
>(
user_data);
1718 NSData* reply_data = nil;
1719 if (data !=
nullptr && data_size > 0) {
1720 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1722 captures->reply(reply_data);
1727 _engine, message_reply, captures.get(), &response_handle);
1729 NSLog(
@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1739 .message_size =
message.length,
1740 .response_handle = response_handle,
1743 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1745 NSLog(
@"Failed to send message to Flutter engine on channel '%@' (%d).",
channel,
1749 if (response_handle !=
nullptr) {
1751 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1753 NSLog(
@"Failed to release the response handle (%d).", release_result);
1759 binaryMessageHandler:
1764 handler:[handler copy]];
1771 NSString* foundChannel = nil;
1774 if ([handlerInfo.
connection isEqual:@(connection)]) {
1780 [_messengerHandlers removeObjectForKey:foundChannel];
1784#pragma mark - FlutterPluginRegistry
1786- (
id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1787 id<FlutterPluginRegistrar> registrar =
self.pluginRegistrars[pluginName];
1791 self.pluginRegistrars[pluginName] = registrarImpl;
1792 registrar = registrarImpl;
1797- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1801#pragma mark - FlutterTextureRegistrar
1804 return [_renderer registerTexture:texture];
1807- (
BOOL)registerTextureWithID:(int64_t)textureId {
1808 return _embedderAPI.RegisterExternalTexture(_engine, textureId) ==
kSuccess;
1811- (void)textureFrameAvailable:(int64_t)textureID {
1812 [_renderer textureFrameAvailable:textureID];
1815- (
BOOL)markTextureFrameAvailable:(int64_t)textureID {
1816 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) ==
kSuccess;
1819- (void)unregisterTexture:(int64_t)textureID {
1820 [_renderer unregisterTexture:textureID];
1823- (
BOOL)unregisterTextureWithID:(int64_t)textureID {
1824 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) ==
kSuccess;
1827#pragma mark - Task runner integration
1829- (void)postMainThreadTask:(
FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1832 const auto engine_time = _embedderAPI.GetCurrentTime();
1833 [FlutterRunLoop.mainRunLoop
1834 performAfterDelay:(targetTime - (double)engine_time) / NSEC_PER_SEC
1837 if (self != nil && self->_engine != nil) {
1838 auto result = _embedderAPI.RunTask(self->_engine, &task);
1839 if (result != kSuccess) {
1840 NSLog(@"Could not post a task to the Flutter engine.");
1847- (
flutter::FlutterCompositor*)macOSCompositor {
1851#pragma mark - FlutterKeyboardManagerDelegate
1858 userData:(
void*)userData {
1859 _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
NSInteger clearContents()
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
#define FLUTTER_API_SYMBOL(symbol)
@ kUnfocused
Specifies that a view does not have platform focus.
@ kFocused
Specifies that a view has platform focus.
@ kFlutterEngineAOTDataSourceTypeElfPath
void(* FlutterPlatformMessageCallback)(const FlutterPlatformMessage *, void *)
@ kFlutterEngineDisplaysUpdateTypeStartup
FlutterThreadPriority
Valid values for priority of Thread.
@ kDisplay
Suitable for threads which generate data for the display.
@ kRaster
Suitable for thread which raster data.
void(* FlutterKeyEventCallback)(bool, void *)
FlutterEngineResult(* FlutterEngineSendPlatformMessageResponseFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessageResponseHandle *handle, const uint8_t *data, size_t data_length)
#define FLUTTER_ENGINE_VERSION
const char FlTextDirection FlAssertiveness assertiveness
const gchar FlBinaryMessengerMessageHandler handler
const uint8_t uint32_t uint32_t GError ** error
G_BEGIN_DECLS FlutterViewId view_id
HWND(* FlutterPlatformViewFactory)(const FlutterPlatformViewCreationParameters *)
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
#define FML_DCHECK(condition)
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
void(* rootIsolateCreateCallback)(void *_Nullable)
NSString * lookupKeyForAsset:fromPackage:(NSString *asset,[fromPackage] NSString *package)
NSString * lookupKeyForAsset:(NSString *asset)
instancetype displayLinkWithView:(NSView *view)
FlutterBinaryMessageHandler handler
NSObject< FlutterBinaryMessenger > * binaryMessenger
NSObject * publishedValue
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
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)
instancetype typedDataWithBytes:(NSData *data)
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)
FlutterBinaryMessengerRelay * _binaryMessenger
FlutterViewController * viewController
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
FlutterViewIdentifier _nextViewIdentifier
NSString *const kFlutterPlatformChannel
FlutterMethodChannel * _screenshotChannel
FlutterTextInputPlugin * _textInputPlugin
FlutterDartProject * _project
NSMapTable * _viewControllers
FlutterCompositor _compositor
__weak FlutterView * _lastViewWithPointerEvent
FlutterKeyboardManager * _keyboardManager
FlutterWindowController * _windowController
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)
constexpr int64_t kFlutterImplicitViewId
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
std::vector< std::string > GetSwitchesFromEnvironment()
instancetype sharedInstance()
impeller::ShaderType type
void * user_data
The |FlutterAddViewInfo.user_data|.
FlutterBackingStoreCreateCallback create_backing_store_callback
bool avoid_backing_store_cache
size_t struct_size
This size of this struct. Must be sizeof(FlutterCompositor).
FlutterPresentViewCallback present_view_callback
FlutterBackingStoreCollectCallback collect_backing_store_callback
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
FlutterEngineAOTDataSourceType type
const char * elf_path
Absolute path to an ELF library file.
size_t height
The height of the display, in physical pixels.
double device_pixel_ratio
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineDisplay).
size_t width
The width of the display, in physical pixels.
FlutterEngineDisplayId display_id
Function-pointer-based versions of the APIs above.
const char * language_code
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
const char * country_code
const char * variant_code
FlutterPlatformMessageCallback platform_message_callback
FlutterLogMessageCallback log_message_callback
FlutterViewFocusChangeRequestCallback view_focus_change_request_callback
VsyncCallback vsync_callback
OnPreEngineRestartCallback on_pre_engine_restart_callback
FlutterEngineAOTData aot_data
const char *const * dart_entrypoint_argv
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
FlutterUpdateSemanticsCallback2 update_semantics_callback2
const char *const * command_line_argv
const char * icu_data_path
bool shutdown_dart_vm_when_done
const char * custom_dart_entrypoint
const FlutterCustomTaskRunners * custom_task_runners
int command_line_argc
The command line argument count used to initialize the project.
VoidCallback root_isolate_create_callback
const FlutterCompositor * compositor
FlutterRemoveViewCallback remove_view_callback
A batch of updates to semantics nodes and custom actions.
size_t struct_size
The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
FlutterViewFocusState state
The focus state of the view.
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
size_t min_height_constraint
size_t min_width_constraint
size_t max_width_constraint
size_t max_height_constraint