Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
FlutterEngine.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
7
8#include <algorithm>
9#include <iostream>
10#include <sstream>
11#include <vector>
12
17
18#import "flutter/shell/platform/darwin/common/InternalFlutterSwiftCommon/InternalFlutterSwiftCommon.h"
20#import "flutter/shell/platform/darwin/macos/InternalFlutterSwift/InternalFlutterSwift.h"
35
36#import <CoreVideo/CoreVideo.h>
37#import <IOSurface/IOSurface.h>
38
40
41NSString* const kFlutterPlatformChannel = @"flutter/platform";
42NSString* const kFlutterSettingsChannel = @"flutter/settings";
43NSString* const kFlutterLifecycleChannel = @"flutter/lifecycle";
44
46
47/**
48 * Constructs and returns a FlutterLocale struct corresponding to |locale|, which must outlive
49 * the returned struct.
50 */
51static FlutterLocale FlutterLocaleFromNSLocale(NSLocale* locale) {
52 FlutterLocale flutterLocale = {};
53 flutterLocale.struct_size = sizeof(FlutterLocale);
54 flutterLocale.language_code = [[locale objectForKey:NSLocaleLanguageCode] UTF8String];
55 flutterLocale.country_code = [[locale objectForKey:NSLocaleCountryCode] UTF8String];
56 flutterLocale.script_code = [[locale objectForKey:NSLocaleScriptCode] UTF8String];
57 flutterLocale.variant_code = [[locale objectForKey:NSLocaleVariantCode] UTF8String];
58 return flutterLocale;
59}
60
61/// The private notification for voice over.
62static NSString* const kEnhancedUserInterfaceNotification =
63 @"NSApplicationDidChangeAccessibilityEnhancedUserInterfaceNotification";
64static NSString* const kEnhancedUserInterfaceKey = @"AXEnhancedUserInterface";
65
66/// Clipboard plain text format.
67constexpr char kTextPlainFormat[] = "text/plain";
68
69#pragma mark -
70
71// Records an active handler of the messenger (FlutterEngine) that listens to
72// platform messages on a given channel.
73@interface FlutterEngineHandlerInfo : NSObject
74
75- (instancetype)initWithConnection:(NSNumber*)connection
76 handler:(FlutterBinaryMessageHandler)handler;
77
78@property(nonatomic, readonly) FlutterBinaryMessageHandler handler;
79@property(nonatomic, readonly) NSNumber* connection;
80
81@end
82
83@implementation FlutterEngineHandlerInfo
84- (instancetype)initWithConnection:(NSNumber*)connection
85 handler:(FlutterBinaryMessageHandler)handler {
86 self = [super init];
87 NSAssert(self, @"Super init cannot be nil");
89 _handler = handler;
90 return self;
91}
92@end
93
94#pragma mark -
95
96/**
97 * Private interface declaration for FlutterEngine.
98 */
99@interface FlutterEngine () <FlutterBinaryMessenger,
100 FlutterMouseCursorPluginDelegate,
101 FlutterKeyboardManagerDelegate,
102 FlutterTextInputPluginDelegate>
103
104/**
105 * A mutable array that holds one bool value that determines if responses to platform messages are
106 * clear to execute. This value should be read or written only inside of a synchronized block and
107 * will return `NO` after the FlutterEngine has been dealloc'd.
108 */
109@property(nonatomic, strong) NSMutableArray<NSNumber*>* isResponseValid;
110
111/**
112 * All delegates added via plugin calls to addApplicationDelegate.
113 */
114@property(nonatomic, strong) NSPointerArray* pluginAppDelegates;
115
116/**
117 * All registrars returned from registrarForPlugin:
118 */
119@property(nonatomic, readonly)
120 NSMutableDictionary<NSString*, FlutterEngineRegistrar*>* pluginRegistrars;
121
122- (nullable FlutterViewController*)viewControllerForIdentifier:
123 (FlutterViewIdentifier)viewIdentifier;
124
125/**
126 * An internal method that adds the view controller with the given ID.
127 *
128 * This method assigns the controller with the ID, puts the controller into the
129 * map, and does assertions related to the implicit view ID.
130 */
131- (void)registerViewController:(FlutterViewController*)controller
132 forIdentifier:(FlutterViewIdentifier)viewIdentifier;
133
134/**
135 * An internal method that removes the view controller with the given ID.
136 *
137 * This method clears the ID of the controller, removes the controller from the
138 * map. This is an no-op if the view ID is not associated with any view
139 * controllers.
140 */
141- (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier;
142
143/**
144 * Shuts down the engine if view requirement is not met, and headless execution
145 * is not allowed.
146 */
147- (void)shutDownIfNeeded;
148
149/**
150 * Sends the list of user-preferred locales to the Flutter engine.
151 */
152- (void)sendUserLocales;
153
154/**
155 * Handles a platform message from the engine.
156 */
157- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
158
159/**
160 * Requests that the task be posted back the to the Flutter engine at the target time. The target
161 * time is in the clock used by the Flutter engine.
162 */
163- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime;
164
165/**
166 * Loads the AOT snapshots and instructions from the elf bundle (app_elf_snapshot.so) into _aotData,
167 * if it is present in the assets directory.
168 */
169- (void)loadAOTData:(NSString*)assetsDir;
170
171/**
172 * Creates a platform view channel and sets up the method handler.
173 */
174- (void)setUpPlatformViewChannel;
175
176/**
177 * Creates an accessibility channel and sets up the message handler.
178 */
179- (void)setUpAccessibilityChannel;
180
181/**
182 * Handles messages received from the Flutter engine on the _*Channel channels.
183 */
184- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result;
185
186@end
187
188#pragma mark -
189
191 __weak FlutterEngine* _engine;
193}
194
195- (instancetype)initWithEngine:(FlutterEngine*)engine
196 terminator:(FlutterTerminationCallback)terminator {
197 self = [super init];
198 _acceptingRequests = NO;
199 _engine = engine;
200 _terminator = terminator ? terminator : ^(id sender) {
201 // Default to actually terminating the application. The terminator exists to
202 // allow tests to override it so that an actual exit doesn't occur.
203 [[NSApplication sharedApplication] terminate:sender];
204 };
205 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
206 if ([appDelegate respondsToSelector:@selector(setTerminationHandler:)]) {
207 FlutterAppDelegate* flutterAppDelegate = reinterpret_cast<FlutterAppDelegate*>(appDelegate);
208 flutterAppDelegate.terminationHandler = self;
209 }
210 return self;
211}
212
213// This is called by the method call handler in the engine when the application
214// requests termination itself.
215- (void)handleRequestAppExitMethodCall:(NSDictionary<NSString*, id>*)arguments
216 result:(FlutterResult)result {
217 NSString* type = arguments[@"type"];
218 // Ignore the "exitCode" value in the arguments because AppKit doesn't have
219 // any good way to set the process exit code other than calling exit(), and
220 // that bypasses all of the native applicationShouldExit shutdown events,
221 // etc., which we don't want to skip.
222
223 FlutterAppExitType exitType =
224 [type isEqualTo:@"cancelable"] ? kFlutterAppExitTypeCancelable : kFlutterAppExitTypeRequired;
225
226 [self requestApplicationTermination:[NSApplication sharedApplication]
227 exitType:exitType
228 result:result];
229}
230
231// This is called by the FlutterAppDelegate whenever any termination request is
232// received.
233- (void)requestApplicationTermination:(id)sender
234 exitType:(FlutterAppExitType)type
235 result:(nullable FlutterResult)result {
236 _shouldTerminate = YES;
237 if (![self acceptingRequests]) {
238 // Until the Dart application has signaled that it is ready to handle
239 // termination requests, the app will just terminate when asked.
240 type = kFlutterAppExitTypeRequired;
241 }
242 switch (type) {
243 case kFlutterAppExitTypeCancelable: {
244 FlutterJSONMethodCodec* codec = [FlutterJSONMethodCodec sharedInstance];
245 FlutterMethodCall* methodCall =
246 [FlutterMethodCall methodCallWithMethodName:@"System.requestAppExit" arguments:nil];
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]]) {
253 FlutterError* error = (FlutterError*)decoded_reply;
254 NSLog(@"Method call returned error[%@]: %@ %@", [error code], [error message],
255 [error details]);
256 _terminator(sender);
257 return;
258 }
259 if (![decoded_reply isKindOfClass:[NSDictionary class]]) {
260 NSLog(@"Call to System.requestAppExit returned an unexpected object: %@",
261 decoded_reply);
262 _terminator(sender);
263 return;
264 }
265 NSDictionary* replyArgs = (NSDictionary*)decoded_reply;
266 if ([replyArgs[@"response"] isEqual:@"exit"]) {
267 _terminator(sender);
268 } else if ([replyArgs[@"response"] isEqual:@"cancel"]) {
269 _shouldTerminate = NO;
270 }
271 if (result != nil) {
272 result(replyArgs);
273 }
274 }];
275 break;
276 }
277 case kFlutterAppExitTypeRequired:
278 NSAssert(_terminator, @"terminator shouldn't be nil");
279 _terminator(sender);
280 break;
281 }
282}
283
284@end
285
286#pragma mark -
287
288@implementation FlutterPasteboard
289
290- (NSInteger)clearContents {
291 return [[NSPasteboard generalPasteboard] clearContents];
292}
293
294- (NSString*)stringForType:(NSPasteboardType)dataType {
295 return [[NSPasteboard generalPasteboard] stringForType:dataType];
296}
297
298- (BOOL)setString:(nonnull NSString*)string forType:(nonnull NSPasteboardType)dataType {
299 return [[NSPasteboard generalPasteboard] setString:string forType:dataType];
300}
301
302@end
303
304#pragma mark -
305
306/**
307 * `FlutterPluginRegistrar` implementation handling a single plugin.
308 */
310- (instancetype)initWithPlugin:(nonnull NSString*)pluginKey
311 flutterEngine:(nonnull FlutterEngine*)flutterEngine;
312
313- (nullable NSView*)viewForIdentifier:(FlutterViewIdentifier)viewIdentifier;
314
315/**
316 * The value published by this plugin, or NSNull if nothing has been published.
317 *
318 * The unusual NSNull is for the documented behavior of valuePublishedByPlugin:.
319 */
320@property(nonatomic, readonly, nonnull) NSObject* publishedValue;
321@end
322
323@implementation FlutterEngineRegistrar {
324 NSString* _pluginKey;
326}
327
328@dynamic view;
329
330- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine {
331 self = [super init];
332 if (self) {
333 _pluginKey = [pluginKey copy];
334 _flutterEngine = flutterEngine;
335 _publishedValue = [NSNull null];
336 }
337 return self;
338}
339
340#pragma mark - FlutterPluginRegistrar
341
342- (id<FlutterBinaryMessenger>)messenger {
344}
345
346- (id<FlutterTextureRegistry>)textures {
347 return _flutterEngine.renderer;
348}
349
350- (NSView*)view {
351 return [self viewForIdentifier:kFlutterImplicitViewId];
352}
353
354- (NSView*)viewForIdentifier:(FlutterViewIdentifier)viewIdentifier {
355 FlutterViewController* controller = [_flutterEngine viewControllerForIdentifier:viewIdentifier];
356 if (controller == nil) {
357 return nil;
358 }
359 if (!controller.viewLoaded) {
360 [controller loadView];
361 }
362 return controller.flutterView;
363}
364
365- (NSViewController*)viewController {
366 return [_flutterEngine viewControllerForIdentifier:kFlutterImplicitViewId];
367}
368
369- (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate
370 channel:(nonnull FlutterMethodChannel*)channel {
371 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
372 [delegate handleMethodCall:call result:result];
373 }];
374}
375
376- (void)addApplicationDelegate:(NSObject<FlutterAppLifecycleDelegate>*)delegate {
377 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
378 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
379 id<FlutterAppLifecycleProvider> lifeCycleProvider =
380 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
381 [lifeCycleProvider addApplicationLifecycleDelegate:delegate];
382 [_flutterEngine.pluginAppDelegates addPointer:(__bridge void*)delegate];
383 }
384}
385
386- (void)registerViewFactory:(nonnull NSObject<FlutterPlatformViewFactory>*)factory
387 withId:(nonnull NSString*)factoryId {
388 [[_flutterEngine platformViewController] registerViewFactory:factory withId:factoryId];
389}
390
391- (void)publish:(NSObject*)value {
392 _publishedValue = value;
393}
394
395- (NSString*)lookupKeyForAsset:(NSString*)asset {
397}
398
399- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
400 return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
401}
402
403@end
404
405// Callbacks provided to the engine. See the called methods for documentation.
406#pragma mark - Static methods provided to engine configuration
407
410 [engine engineCallbackOnPlatformMessage:message];
411}
412
413#pragma mark -
414
415@implementation FlutterEngine {
416 // The embedding-API-level engine object.
418
419 // The project being run by this engine.
421
422 // A mapping of channel names to the registered information for those channels.
423 NSMutableDictionary<NSString*, FlutterEngineHandlerInfo*>* _messengerHandlers;
424
425 // A self-incremental integer to assign to newly assigned channels as
426 // identification.
428
429 // Whether the engine can continue running after the view controller is removed.
431
432 // Pointer to the Dart AOT snapshot and instruction data.
434
435 // _macOSCompositor is created when the engine is created and its destruction is handled by ARC
436 // when the engine is destroyed.
437 std::unique_ptr<flutter::FlutterCompositor> _macOSCompositor;
438
439 // The information of all views attached to this engine mapped from IDs.
440 //
441 // It can't use NSDictionary, because the values need to be weak references.
442 NSMapTable* _viewControllers;
443
444 // FlutterCompositor is copied and used in embedder.cc.
446
447 // Method channel for platform view functions. These functions include creating, disposing and
448 // mutating a platform view.
450
451 // Used to support creation and deletion of platform views and registering platform view
452 // factories. Lifecycle is tied to the engine.
454
455 // Used to manage Flutter windows created by the Dart application
457
458 // A message channel for sending user settings to the flutter engine.
460
461 // A message channel for accessibility.
463
464 // A method channel for miscellaneous platform functionality.
466
467 // A method channel for taking screenshots via the rasterizer.
469
470 // Whether the application is currently the active application.
472
473 // Whether any portion of the application is currently visible.
475
476 // Proxy to allow plugins, channels to hold a weak reference to the binary messenger (self).
478
479 // Map from ViewId to vsync waiter. Note that this is modified on main thread
480 // but accessed on UI thread, so access must be @synchronized.
481 NSMapTable<NSNumber*, FlutterVSyncWaiter*>* _vsyncWaiters;
482
483 // Weak reference to last view that received a pointer event. This is used to
484 // pair cursor change with a view.
486
487 // Pointer to a keyboard manager.
489
490 // The text input plugin that handles text editing state for text fields.
492
493 // Whether the engine is running in multi-window mode. This affects behavior
494 // when adding view controller (it will fail when calling multiple times without
495 // _multiviewEnabled).
497
498 // View identifier for the next view to be created.
499 // Only used when multiview is enabled.
501}
502
503@synthesize windowController = _windowController;
504@synthesize project = _project;
505
506- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project {
507 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
508}
509
510static const int kMainThreadPriority = 47;
511
512static void SetThreadPriority(FlutterThreadPriority priority) {
513 if (priority == kDisplay || priority == kRaster) {
514 pthread_t thread = pthread_self();
515 sched_param param;
516 int policy;
517 if (!pthread_getschedparam(thread, &policy, &param)) {
518 param.sched_priority = kMainThreadPriority;
519 pthread_setschedparam(thread, policy, &param);
520 }
521 pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0);
522 }
523}
524
525- (instancetype)initWithName:(NSString*)labelPrefix
526 project:(FlutterDartProject*)project
527 allowHeadlessExecution:(BOOL)allowHeadlessExecution {
528 self = [super init];
529 NSAssert(self, @"Super init cannot be nil");
530
531 [FlutterRunLoop ensureMainLoopInitialized];
532
533 _pasteboard = [[FlutterPasteboard alloc] init];
534 _active = NO;
535 _visible = NO;
536 _project = project ?: [[FlutterDartProject alloc] init];
537 _messengerHandlers = [[NSMutableDictionary alloc] init];
538 _pluginAppDelegates = [NSPointerArray weakObjectsPointerArray];
539 _pluginRegistrars = [[NSMutableDictionary alloc] init];
541 _allowHeadlessExecution = allowHeadlessExecution;
542 _semanticsEnabled = NO;
543 _binaryMessenger = [[FlutterBinaryMessengerRelay alloc] initWithParent:self];
544 _isResponseValid = [[NSMutableArray alloc] initWithCapacity:1];
545 [_isResponseValid addObject:@YES];
546 _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self];
547 _textInputPlugin = [[FlutterTextInputPlugin alloc] initWithDelegate:self];
550
551 _embedderAPI.struct_size = sizeof(FlutterEngineProcTable);
552 FlutterEngineGetProcAddresses(&_embedderAPI);
553
554 _viewControllers = [NSMapTable weakToWeakObjectsMapTable];
555 _renderer = [[FlutterRenderer alloc] initWithFlutterEngine:self];
556
557 NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter];
558 [notificationCenter addObserver:self
559 selector:@selector(sendUserLocales)
560 name:NSCurrentLocaleDidChangeNotification
561 object:nil];
562
564 // The macOS compositor must be initialized in the initializer because it is
565 // used when adding views, which might happen before runWithEntrypoint.
566 _macOSCompositor = std::make_unique<flutter::FlutterCompositor>(
567 [[FlutterViewEngineProvider alloc] initWithEngine:self],
568 [[FlutterTimeConverter alloc] initWithEngine:self], _platformViewController);
569
570 [self setUpPlatformViewChannel];
571
574
575 [self setUpAccessibilityChannel];
576 [self setUpNotificationCenterListeners];
577 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
578 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
579 _terminationHandler = [[FlutterEngineTerminationHandler alloc] initWithEngine:self
580 terminator:nil];
581 id<FlutterAppLifecycleProvider> lifecycleProvider =
582 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
583 [lifecycleProvider addApplicationLifecycleDelegate:self];
584 } else {
585 _terminationHandler = nil;
586 }
587
588 _vsyncWaiters = [NSMapTable strongToStrongObjectsMapTable];
589
590 return self;
591}
592
593- (void)dealloc {
594 id<NSApplicationDelegate> appDelegate = [[NSApplication sharedApplication] delegate];
595 if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifecycleProvider)]) {
596 id<FlutterAppLifecycleProvider> lifecycleProvider =
597 static_cast<id<FlutterAppLifecycleProvider>>(appDelegate);
598 [lifecycleProvider removeApplicationLifecycleDelegate:self];
599
600 // Unregister any plugins that registered as app delegates, since they are not guaranteed to
601 // live after the engine is destroyed, and their delegation registration is intended to be bound
602 // to the engine and its lifetime.
603 for (id<FlutterAppLifecycleDelegate> delegate in _pluginAppDelegates) {
604 if (delegate) {
605 [lifecycleProvider removeApplicationLifecycleDelegate:delegate];
606 }
607 }
608 }
609 // Clear any published values, just in case a plugin has created a retain cycle with the
610 // registrar.
611 for (NSString* pluginName in _pluginRegistrars) {
612 [_pluginRegistrars[pluginName] publish:[NSNull null]];
613 }
614 @synchronized(_isResponseValid) {
615 [_isResponseValid removeAllObjects];
616 [_isResponseValid addObject:@NO];
617 }
618 [self shutDownEngine];
619 if (_aotData) {
620 _embedderAPI.CollectAOTData(_aotData);
621 }
622}
623
624- (FlutterTaskRunnerDescription)createPlatformThreadTaskDescription {
625 static size_t sTaskRunnerIdentifiers = 0;
626 FlutterTaskRunnerDescription cocoa_task_runner_description = {
628 // Retain for use in post_task_callback. Released in destruction_callback.
629 .user_data = (__bridge_retained void*)self,
630 .runs_task_on_current_thread_callback = [](void* user_data) -> bool {
631 return [[NSThread currentThread] isMainThread];
632 },
633 .post_task_callback = [](FlutterTask task, uint64_t target_time_nanos,
634 void* user_data) -> void {
636 [engine postMainThreadTask:task targetTimeInNanoseconds:target_time_nanos];
637 },
638 .identifier = ++sTaskRunnerIdentifiers,
639 .destruction_callback =
640 [](void* user_data) {
641 // Balancing release for the retain when setting user_data above.
642 FlutterEngine* engine = (__bridge_transfer FlutterEngine*)user_data;
643 engine = nil;
644 },
645 };
646 return cocoa_task_runner_description;
647}
648
649- (void)onFocusChangeRequest:(const FlutterViewFocusChangeRequest*)request {
650 FlutterViewController* controller = [self viewControllerForIdentifier:request->view_id];
651 if (controller == nil) {
652 return;
653 }
654 if (request->state == kFocused) {
655 [controller.flutterView.window makeFirstResponder:controller.flutterView];
656 }
657}
658
659- (BOOL)runWithEntrypoint:(NSString*)entrypoint {
660 if (self.running) {
661 return NO;
662 }
663
664 if (!_allowHeadlessExecution && [_viewControllers count] == 0) {
665 NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
666 return NO;
667 }
668
669 [self addInternalPlugins];
670
671 // The first argument of argv is required to be the executable name.
672 std::vector<const char*> argv = {[self.executableName UTF8String]};
673 std::vector<std::string> switches = self.switches;
674
675 // Enable Impeller only if specifically asked for from the project or cmdline arguments.
677 std::find(switches.begin(), switches.end(), "--enable-impeller=true") != switches.end()) {
678 switches.push_back("--enable-impeller=true");
679 }
680
681 if (_project.enableSDFs ||
682 std::find(switches.begin(), switches.end(), "--impeller-use-sdfs=true") != switches.end()) {
683 switches.push_back("--impeller-use-sdfs=true");
684 }
685
687 std::find(switches.begin(), switches.end(), "--enable-flutter-gpu=true") != switches.end()) {
688 switches.push_back("--enable-flutter-gpu=true");
689 }
690
691 std::transform(switches.begin(), switches.end(), std::back_inserter(argv),
692 [](const std::string& arg) -> const char* { return arg.c_str(); });
693
694 std::vector<const char*> dartEntrypointArgs;
695 for (NSString* argument in [_project dartEntrypointArguments]) {
696 dartEntrypointArgs.push_back([argument UTF8String]);
697 }
698
699 FlutterProjectArgs flutterArguments = {};
700 flutterArguments.struct_size = sizeof(FlutterProjectArgs);
701 flutterArguments.assets_path = _project.assetsPath.UTF8String;
702 flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String;
703 flutterArguments.command_line_argc = static_cast<int>(argv.size());
704 flutterArguments.command_line_argv = argv.empty() ? nullptr : argv.data();
706 flutterArguments.update_semantics_callback2 = [](const FlutterSemanticsUpdate2* update,
707 void* user_data) {
708 // TODO(dkwingsmt): This callback only supports single-view, therefore it
709 // only operates on the implicit view. To support multi-view, we need a
710 // way to pass in the ID (probably through FlutterSemanticsUpdate).
712 [[engine viewControllerForIdentifier:kFlutterImplicitViewId] updateSemantics:update];
713 };
714 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
715 flutterArguments.shutdown_dart_vm_when_done = true;
716 flutterArguments.dart_entrypoint_argc = dartEntrypointArgs.size();
717 flutterArguments.dart_entrypoint_argv = dartEntrypointArgs.data();
719 flutterArguments.log_message_callback = [](const char* tag, const char* message,
720 void* user_data) {
721 std::stringstream stream;
722 if (tag && tag[0]) {
723 stream << tag << ": ";
724 }
725 stream << message;
726 std::string log = stream.str();
727 [FlutterLogger logDirect:[NSString stringWithUTF8String:log.c_str()]];
728 };
729
730 flutterArguments.engine_id = reinterpret_cast<int64_t>((__bridge void*)self);
731 flutterArguments.enable_wide_gamut = _project.enableWideGamut;
732
733 BOOL mergedPlatformUIThread = YES;
734 NSNumber* enableMergedPlatformUIThread =
735 [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableMergedPlatformUIThread"];
736 if (enableMergedPlatformUIThread != nil) {
737 mergedPlatformUIThread = enableMergedPlatformUIThread.boolValue;
738 }
739
740 if (mergedPlatformUIThread) {
741 NSLog(@"Running with merged UI and platform thread. Experimental.");
742 }
743
744 // The task description needs to be created separately for platform task
745 // runner and UI task runner because each one has their own __bridge_retained
746 // engine user data.
747 FlutterTaskRunnerDescription platformTaskRunnerDescription =
748 [self createPlatformThreadTaskDescription];
749 std::optional<FlutterTaskRunnerDescription> uiTaskRunnerDescription;
750 if (mergedPlatformUIThread) {
751 uiTaskRunnerDescription = [self createPlatformThreadTaskDescription];
752 }
753
754 const FlutterCustomTaskRunners custom_task_runners = {
756 .platform_task_runner = &platformTaskRunnerDescription,
757 .thread_priority_setter = SetThreadPriority,
758 .ui_task_runner = uiTaskRunnerDescription ? &uiTaskRunnerDescription.value() : nullptr,
759 };
760 flutterArguments.custom_task_runners = &custom_task_runners;
761
762 [self loadAOTData:_project.assetsPath];
763 if (_aotData) {
764 flutterArguments.aot_data = _aotData;
765 }
766
767 flutterArguments.compositor = [self createFlutterCompositor];
768
769 flutterArguments.on_pre_engine_restart_callback = [](void* user_data) {
771 [engine engineCallbackOnPreEngineRestart];
772 };
773
774 flutterArguments.vsync_callback = [](void* user_data, intptr_t baton) {
776 [engine onVSync:baton];
777 };
778
779 flutterArguments.view_focus_change_request_callback =
780 [](const FlutterViewFocusChangeRequest* request, void* user_data) {
782 [engine onFocusChangeRequest:request];
783 };
784
785 FlutterRendererConfig rendererConfig = [_renderer createRendererConfig];
786 FlutterEngineResult result = _embedderAPI.Initialize(
787 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
788 if (result != kSuccess) {
789 NSLog(@"Failed to initialize Flutter engine: error %d", result);
790 return NO;
791 }
792
793 result = _embedderAPI.RunInitialized(_engine);
794 if (result != kSuccess) {
795 NSLog(@"Failed to run an initialized engine: error %d", result);
796 return NO;
797 }
798
799 [self sendUserLocales];
800
801 // Update window metric for all view controllers.
802 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
803 FlutterViewController* nextViewController;
804 while ((nextViewController = [viewControllerEnumerator nextObject])) {
805 [self updateWindowMetricsForViewController:nextViewController];
806 }
807
808 [self updateDisplayConfig];
809 // Send the initial user settings such as brightness and text scale factor
810 // to the engine.
811 [self sendInitialSettings];
812 return YES;
813}
814
815- (void)loadAOTData:(NSString*)assetsDir {
816 if (!_embedderAPI.RunsAOTCompiledDartCode()) {
817 return;
818 }
819
820 BOOL isDirOut = false; // required for NSFileManager fileExistsAtPath.
821 NSFileManager* fileManager = [NSFileManager defaultManager];
822
823 // This is the location where the test fixture places the snapshot file.
824 // For applications built by Flutter tool, this is in "App.framework".
825 NSString* elfPath = [NSString pathWithComponents:@[ assetsDir, @"app_elf_snapshot.so" ]];
826
827 if (![fileManager fileExistsAtPath:elfPath isDirectory:&isDirOut]) {
828 return;
829 }
830
831 FlutterEngineAOTDataSource source = {};
833 source.elf_path = [elfPath cStringUsingEncoding:NSUTF8StringEncoding];
834
835 auto result = _embedderAPI.CreateAOTData(&source, &_aotData);
836 if (result != kSuccess) {
837 NSLog(@"Failed to load AOT data from: %@", elfPath);
838 }
839}
840
841- (void)registerViewController:(FlutterViewController*)controller
842 forIdentifier:(FlutterViewIdentifier)viewIdentifier {
843 _macOSCompositor->AddView(viewIdentifier);
844 NSAssert(controller != nil, @"The controller must not be nil.");
845 if (!_multiViewEnabled) {
846 NSAssert(controller.engine == nil,
847 @"The FlutterViewController is unexpectedly attached to "
848 @"engine %@ before initialization.",
849 controller.engine);
850 }
851 NSAssert([_viewControllers objectForKey:@(viewIdentifier)] == nil,
852 @"The requested view ID is occupied.");
853 [_viewControllers setObject:controller forKey:@(viewIdentifier)];
854 [controller setUpWithEngine:self viewIdentifier:viewIdentifier];
855 NSAssert(controller.viewIdentifier == viewIdentifier, @"Failed to assign view ID.");
856 // Verify that the controller's property are updated accordingly. Failing the
857 // assertions is likely because either the FlutterViewController or the
858 // FlutterEngine is mocked. Please subclass these classes instead.
859 NSAssert(controller.attached, @"The FlutterViewController should switch to the attached mode "
860 @"after it is added to a FlutterEngine.");
861 NSAssert(controller.engine == self,
862 @"The FlutterViewController was added to %@, but its engine unexpectedly became %@.",
863 self, controller.engine);
864
865 if (controller.viewLoaded) {
866 [self viewControllerViewDidLoad:controller];
867 }
868
869 if (viewIdentifier != kFlutterImplicitViewId) {
870 // These will be overriden immediately after the FlutterView is created
871 // by actual values.
874 .width = 0,
875 .height = 0,
876 .pixel_ratio = 1.0,
877 };
878 bool added = false;
880 .view_id = viewIdentifier,
881 .view_metrics = &metrics,
882 .user_data = &added,
883 .add_view_callback = [](const FlutterAddViewResult* r) {
884 auto added = reinterpret_cast<bool*>(r->user_data);
885 *added = true;
886 }};
887 // The callback should be called synchronously from platform thread.
888 _embedderAPI.AddView(_engine, &info);
889 FML_DCHECK(added);
890 if (!added) {
891 NSLog(@"Failed to add view with ID %llu", viewIdentifier);
892 }
893 }
894}
895
896- (void)viewControllerViewDidLoad:(FlutterViewController*)viewController {
897 __weak FlutterEngine* weakSelf = self;
898 FlutterTimeConverter* timeConverter = [[FlutterTimeConverter alloc] initWithEngine:self];
899 FlutterVSyncWaiter* waiter = [[FlutterVSyncWaiter alloc]
900 initWithDisplayLink:[FlutterDisplayLink displayLinkWithView:viewController.view]
901 block:^(CFTimeInterval timestamp, CFTimeInterval targetTimestamp,
902 uintptr_t baton) {
903 uint64_t timeNanos = [timeConverter CAMediaTimeToEngineTime:timestamp];
904 uint64_t targetTimeNanos =
905 [timeConverter CAMediaTimeToEngineTime:targetTimestamp];
906 FlutterEngine* engine = weakSelf;
907 if (engine) {
908 engine->_embedderAPI.OnVsync(_engine, baton, timeNanos, targetTimeNanos);
909 }
910 }];
911 @synchronized(_vsyncWaiters) {
912 FML_DCHECK([_vsyncWaiters objectForKey:@(viewController.viewIdentifier)] == nil);
913 [_vsyncWaiters setObject:waiter forKey:@(viewController.viewIdentifier)];
914 }
915}
916
917- (void)deregisterViewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
918 if (viewIdentifier != kFlutterImplicitViewId) {
919 bool removed = false;
921 info.struct_size = sizeof(FlutterRemoveViewInfo);
922 info.view_id = viewIdentifier;
923 info.user_data = &removed;
924 // RemoveViewCallback is not finished synchronously, the remove_view_callback
925 // is called from raster thread when the engine knows for sure that the resources
926 // associated with the view are no longer needed.
928 auto removed = reinterpret_cast<bool*>(r->user_data);
929 [FlutterRunLoop.mainRunLoop performBlock:^{
930 *removed = true;
931 }];
932 };
933 _embedderAPI.RemoveView(_engine, &info);
934 while (!removed) {
935 [[FlutterRunLoop mainRunLoop] pollFlutterMessagesOnce];
936 }
937 }
938
939 _macOSCompositor->RemoveView(viewIdentifier);
940
941 FlutterViewController* controller = [self viewControllerForIdentifier:viewIdentifier];
942 // The controller can be nil. The engine stores only a weak ref, and this
943 // method could have been called from the controller's dealloc.
944 if (controller != nil) {
945 [controller detachFromEngine];
946 NSAssert(!controller.attached,
947 @"The FlutterViewController unexpectedly stays attached after being removed. "
948 @"In unit tests, this is likely because either the FlutterViewController or "
949 @"the FlutterEngine is mocked. Please subclass these classes instead.");
950 }
951 [_viewControllers removeObjectForKey:@(viewIdentifier)];
952
953 FlutterVSyncWaiter* waiter = nil;
954 @synchronized(_vsyncWaiters) {
955 waiter = [_vsyncWaiters objectForKey:@(viewIdentifier)];
956 [_vsyncWaiters removeObjectForKey:@(viewIdentifier)];
957 }
958 [waiter invalidate];
959}
960
961- (void)shutDownIfNeeded {
962 if ([_viewControllers count] == 0 && !_allowHeadlessExecution) {
963 [self shutDownEngine];
964 }
965}
966
967- (FlutterViewController*)viewControllerForIdentifier:(FlutterViewIdentifier)viewIdentifier {
968 FlutterViewController* controller = [_viewControllers objectForKey:@(viewIdentifier)];
969 NSAssert(controller == nil || controller.viewIdentifier == viewIdentifier,
970 @"The stored controller has unexpected view ID.");
971 return controller;
972}
973
974- (void)setViewController:(FlutterViewController*)controller {
975 FlutterViewController* currentController =
976 [_viewControllers objectForKey:@(kFlutterImplicitViewId)];
977 if (currentController == controller) {
978 // From nil to nil, or from non-nil to the same controller.
979 return;
980 }
981 if (currentController == nil && controller != nil) {
982 // From nil to non-nil.
983 NSAssert(controller.engine == nil,
984 @"Failed to set view controller to the engine: "
985 @"The given FlutterViewController is already attached to an engine %@. "
986 @"If you wanted to create an FlutterViewController and set it to an existing engine, "
987 @"you should use FlutterViewController#init(engine:, nibName, bundle:) instead.",
988 controller.engine);
989 [self registerViewController:controller forIdentifier:kFlutterImplicitViewId];
990 } else if (currentController != nil && controller == nil) {
991 NSAssert(currentController.viewIdentifier == kFlutterImplicitViewId,
992 @"The default controller has an unexpected ID %llu", currentController.viewIdentifier);
993 // From non-nil to nil.
994 [self deregisterViewControllerForIdentifier:kFlutterImplicitViewId];
995 [self shutDownIfNeeded];
996 } else {
997 // From non-nil to a different non-nil view controller.
998 NSAssert(NO,
999 @"Failed to set view controller to the engine: "
1000 @"The engine already has an implicit view controller %@. "
1001 @"If you wanted to make the implicit view render in a different window, "
1002 @"you should attach the current view controller to the window instead.",
1003 [_viewControllers objectForKey:@(kFlutterImplicitViewId)]);
1004 }
1005}
1006
1008 return [self viewControllerForIdentifier:kFlutterImplicitViewId];
1009}
1010
1011- (FlutterCompositor*)createFlutterCompositor {
1012 _compositor = {};
1015
1017 FlutterBackingStore* backing_store_out, //
1018 void* user_data //
1019 ) {
1020 return reinterpret_cast<flutter::FlutterCompositor*>(user_data)->CreateBackingStore(
1021 config, backing_store_out);
1022 };
1023
1025 void* user_data //
1026 ) { return true; };
1027
1029 return reinterpret_cast<flutter::FlutterCompositor*>(info->user_data)
1030 ->Present(info->view_id, info->layers, info->layers_count);
1031 };
1032
1034
1035 return &_compositor;
1036}
1037
1038- (id<FlutterBinaryMessenger>)binaryMessenger {
1039 return _binaryMessenger;
1040}
1041
1042#pragma mark - Framework-internal methods
1043
1044- (void)addViewController:(FlutterViewController*)controller {
1045 if (!_multiViewEnabled) {
1046 // When multiview is disabled, the engine will only assign views to the implicit view ID.
1047 // The implicit view ID can be reused if and only if the implicit view is unassigned.
1048 NSAssert(self.viewController == nil,
1049 @"The engine already has a view controller for the implicit view.");
1050 self.viewController = controller;
1051 } else {
1052 // When multiview is enabled, the engine will assign views to a self-incrementing ID.
1053 // The implicit view ID can not be reused.
1054 FlutterViewIdentifier viewIdentifier = _nextViewIdentifier++;
1055 [self registerViewController:controller forIdentifier:viewIdentifier];
1056 }
1057}
1058
1059- (void)enableMultiView {
1060 if (!_multiViewEnabled) {
1061 NSAssert(self.viewController == nil,
1062 @"Multiview can only be enabled before adding any view controllers.");
1063 _multiViewEnabled = YES;
1064 }
1065}
1066
1067- (void)windowDidBecomeKey:(FlutterViewIdentifier)viewIdentifier {
1070 .view_id = viewIdentifier,
1071 .state = kFocused,
1072 .direction = kUndefined,
1073 };
1074 _embedderAPI.SendViewFocusEvent(_engine, &event);
1075}
1076
1077- (void)windowDidResignKey:(FlutterViewIdentifier)viewIdentifier {
1080 .view_id = viewIdentifier,
1081 .state = kUnfocused,
1082 .direction = kUndefined,
1083 };
1084 _embedderAPI.SendViewFocusEvent(_engine, &event);
1085}
1086
1087- (void)removeViewController:(nonnull FlutterViewController*)viewController {
1088 [self deregisterViewControllerForIdentifier:viewController.viewIdentifier];
1089 [self shutDownIfNeeded];
1090}
1091
1092- (BOOL)running {
1093 return _engine != nullptr;
1094}
1095
1096- (void)updateDisplayConfig:(NSNotification*)notification {
1097 [self updateDisplayConfig];
1098}
1099
1100- (NSArray<NSScreen*>*)screens {
1101 return [NSScreen screens];
1102}
1103
1104- (void)updateDisplayConfig {
1105 if (!_engine) {
1106 return;
1107 }
1108
1109 std::vector<FlutterEngineDisplay> displays;
1110 for (NSScreen* screen : [self screens]) {
1111 CGDirectDisplayID displayID =
1112 static_cast<CGDirectDisplayID>([screen.deviceDescription[@"NSScreenNumber"] integerValue]);
1113
1114 double devicePixelRatio = screen.backingScaleFactor;
1115 FlutterEngineDisplay display;
1116 display.struct_size = sizeof(display);
1117 display.display_id = displayID;
1118 display.single_display = false;
1119 display.width = static_cast<size_t>(screen.frame.size.width) * devicePixelRatio;
1120 display.height = static_cast<size_t>(screen.frame.size.height) * devicePixelRatio;
1121 display.device_pixel_ratio = devicePixelRatio;
1122
1123 CVDisplayLinkRef displayLinkRef = nil;
1124 CVReturn error = CVDisplayLinkCreateWithCGDisplay(displayID, &displayLinkRef);
1125
1126 if (error == 0) {
1127 CVTime nominal = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(displayLinkRef);
1128 if (!(nominal.flags & kCVTimeIsIndefinite)) {
1129 double refreshRate = static_cast<double>(nominal.timeScale) / nominal.timeValue;
1130 display.refresh_rate = round(refreshRate);
1131 }
1132 CVDisplayLinkRelease(displayLinkRef);
1133 } else {
1134 display.refresh_rate = 0;
1135 }
1136
1137 displays.push_back(display);
1138 }
1139 _embedderAPI.NotifyDisplayUpdate(_engine, kFlutterEngineDisplaysUpdateTypeStartup,
1140 displays.data(), displays.size());
1141}
1142
1143- (void)onSettingsChanged:(NSNotification*)notification {
1144 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
1145 NSString* brightness =
1146 [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
1147 [_settingsChannel sendMessage:@{
1148 @"platformBrightness" : [brightness isEqualToString:@"Dark"] ? @"dark" : @"light",
1149 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32006.
1150 @"textScaleFactor" : @1.0,
1151 @"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]),
1152 }];
1153}
1154
1155- (void)sendInitialSettings {
1156 // TODO(jonahwilliams): https://github.com/flutter/flutter/issues/32015.
1157 [[NSDistributedNotificationCenter defaultCenter]
1158 addObserver:self
1159 selector:@selector(onSettingsChanged:)
1160 name:@"AppleInterfaceThemeChangedNotification"
1161 object:nil];
1162 [self onSettingsChanged:nil];
1163}
1164
1165- (FlutterEngineProcTable&)embedderAPI {
1166 return _embedderAPI;
1167}
1168
1169- (nonnull NSString*)executableName {
1170 return [[[NSProcessInfo processInfo] arguments] firstObject] ?: @"Flutter";
1171}
1172
1173- (void)updateWindowMetricsForViewController:(FlutterViewController*)viewController {
1174 if (!_engine || !viewController || !viewController.viewLoaded) {
1175 return;
1176 }
1177 NSAssert([self viewControllerForIdentifier:viewController.viewIdentifier] == viewController,
1178 @"The provided view controller is not attached to this engine.");
1179 FlutterView* view = viewController.flutterView;
1180 CGRect scaledBounds = [view convertRectToBacking:view.bounds];
1181 CGSize scaledSize = scaledBounds.size;
1182 double pixelRatio = view.layer.contentsScale;
1183 auto displayId = [view.window.screen.deviceDescription[@"NSScreenNumber"] integerValue];
1184 FlutterWindowMetricsEvent windowMetricsEvent = {
1185 .struct_size = sizeof(windowMetricsEvent),
1186 .width = static_cast<size_t>(scaledSize.width),
1187 .height = static_cast<size_t>(scaledSize.height),
1188 .pixel_ratio = pixelRatio,
1189 .left = static_cast<size_t>(scaledBounds.origin.x),
1190 .top = static_cast<size_t>(scaledBounds.origin.y),
1191 .display_id = static_cast<uint64_t>(displayId),
1192 .view_id = viewController.viewIdentifier,
1193 };
1194 if (view.sizedToContents) {
1195 CGSize maximumContentSize = [view convertSizeToBacking:view.maximumContentSize];
1196 CGSize minimumContentSize = [view convertSizeToBacking:view.minimumContentSize];
1197 windowMetricsEvent.has_constraints = true;
1198 windowMetricsEvent.min_width_constraint = static_cast<size_t>(minimumContentSize.width);
1199 windowMetricsEvent.min_height_constraint = static_cast<size_t>(minimumContentSize.height);
1200 windowMetricsEvent.max_width_constraint = static_cast<size_t>(maximumContentSize.width);
1201 windowMetricsEvent.max_height_constraint = static_cast<size_t>(maximumContentSize.height);
1202 } else {
1203 windowMetricsEvent.min_width_constraint = static_cast<size_t>(scaledSize.width);
1204 windowMetricsEvent.min_height_constraint = static_cast<size_t>(scaledSize.height);
1205 windowMetricsEvent.max_width_constraint = static_cast<size_t>(scaledSize.width);
1206 windowMetricsEvent.max_height_constraint = static_cast<size_t>(scaledSize.height);
1207 }
1208 _embedderAPI.SendWindowMetricsEvent(_engine, &windowMetricsEvent);
1209}
1210
1211- (void)sendPointerEvent:(const FlutterPointerEvent&)event {
1212 _embedderAPI.SendPointerEvent(_engine, &event, 1);
1213 _lastViewWithPointerEvent = [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView;
1214}
1215
1216- (void)setSemanticsEnabled:(BOOL)enabled {
1217 if (_semanticsEnabled == enabled) {
1218 return;
1219 }
1220 _semanticsEnabled = enabled;
1221
1222 // Update all view controllers' bridges.
1223 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1224 FlutterViewController* nextViewController;
1225 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1226 [nextViewController notifySemanticsEnabledChanged];
1227 }
1228
1229 _embedderAPI.UpdateSemanticsEnabled(_engine, _semanticsEnabled);
1230}
1231
1232- (void)dispatchSemanticsAction:(FlutterSemanticsAction)action
1233 toTarget:(uint16_t)target
1234 withData:(fml::MallocMapping)data {
1235 _embedderAPI.DispatchSemanticsAction(_engine, target, action, data.GetMapping(), data.GetSize());
1236}
1237
1238- (FlutterPlatformViewController*)platformViewController {
1240}
1241
1242#pragma mark - Private methods
1243
1244- (void)sendUserLocales {
1245 if (!self.running) {
1246 return;
1247 }
1248
1249 // Create a list of FlutterLocales corresponding to the preferred languages.
1250 NSMutableArray<NSLocale*>* locales = [NSMutableArray array];
1251 std::vector<FlutterLocale> flutterLocales;
1252 flutterLocales.reserve(locales.count);
1253 for (NSString* localeID in [NSLocale preferredLanguages]) {
1254 NSLocale* locale = [[NSLocale alloc] initWithLocaleIdentifier:localeID];
1255 [locales addObject:locale];
1256 flutterLocales.push_back(FlutterLocaleFromNSLocale(locale));
1257 }
1258 // Convert to a list of pointers, and send to the engine.
1259 std::vector<const FlutterLocale*> flutterLocaleList;
1260 flutterLocaleList.reserve(flutterLocales.size());
1261 std::transform(flutterLocales.begin(), flutterLocales.end(),
1262 std::back_inserter(flutterLocaleList),
1263 [](const auto& arg) -> const auto* { return &arg; });
1264 _embedderAPI.UpdateLocales(_engine, flutterLocaleList.data(), flutterLocaleList.size());
1265}
1266
1267- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
1268 NSData* messageData = nil;
1269 if (message->message_size > 0) {
1270 messageData = [NSData dataWithBytesNoCopy:(void*)message->message
1271 length:message->message_size
1272 freeWhenDone:NO];
1273 }
1274 NSString* channel = @(message->channel);
1275 __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle;
1276 __block FlutterEngine* weakSelf = self;
1277 NSMutableArray* isResponseValid = self.isResponseValid;
1278 FlutterEngineSendPlatformMessageResponseFnPtr sendPlatformMessageResponse =
1279 _embedderAPI.SendPlatformMessageResponse;
1280 FlutterBinaryReply binaryResponseHandler = ^(NSData* response) {
1281 @synchronized(isResponseValid) {
1282 if (![isResponseValid[0] boolValue]) {
1283 // Ignore, engine was killed.
1284 return;
1285 }
1286 if (responseHandle) {
1287 sendPlatformMessageResponse(weakSelf->_engine, responseHandle,
1288 static_cast<const uint8_t*>(response.bytes), response.length);
1289 responseHandle = NULL;
1290 } else {
1291 NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response "
1292 "on channel '%@'.",
1293 channel);
1294 }
1295 }
1296 };
1297
1298 FlutterEngineHandlerInfo* handlerInfo = _messengerHandlers[channel];
1299 if (handlerInfo) {
1300 handlerInfo.handler(messageData, binaryResponseHandler);
1301 } else {
1302 binaryResponseHandler(nil);
1303 }
1304}
1305
1306- (void)engineCallbackOnPreEngineRestart {
1307 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1308 FlutterViewController* nextViewController;
1309 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1310 [nextViewController onPreEngineRestart];
1311 }
1312 [_windowController closeAllWindows];
1313 [_platformViewController reset];
1314 _keyboardManager = [[FlutterKeyboardManager alloc] initWithDelegate:self];
1315}
1316
1317// This will be called on UI thread, which maybe or may not be platform thread,
1318// depending on the configuration.
1319- (void)onVSync:(uintptr_t)baton {
1320 auto block = ^{
1321 // TODO(knopp): Use vsync waiter for correct view.
1322 // https://github.com/flutter/flutter/issues/142845
1323 FlutterVSyncWaiter* waiter =
1324 [_vsyncWaiters objectForKey:[_vsyncWaiters.keyEnumerator nextObject]];
1325 if (waiter != nil) {
1326 [waiter waitForVSync:baton];
1327 } else {
1328 // Sometimes there is a vsync request right after the last view is removed.
1329 // It still need to be handled, otherwise the engine will stop producing frames
1330 // even if a new view is added later.
1331 self.embedderAPI.OnVsync(_engine, baton, 0, 0);
1332 }
1333 };
1334 if ([NSThread isMainThread]) {
1335 block();
1336 } else {
1337 [FlutterRunLoop.mainRunLoop performBlock:block];
1338 }
1339}
1340
1341/**
1342 * Note: Called from dealloc. Should not use accessors or other methods.
1343 */
1344- (void)shutDownEngine {
1345 if (_engine == nullptr) {
1346 return;
1347 }
1348
1349 FlutterEngineResult result = _embedderAPI.Deinitialize(_engine);
1350 if (result != kSuccess) {
1351 NSLog(@"Could not de-initialize the Flutter engine: error %d", result);
1352 }
1353
1354 result = _embedderAPI.Shutdown(_engine);
1355 if (result != kSuccess) {
1356 NSLog(@"Failed to shut down Flutter engine: error %d", result);
1357 }
1358 _engine = nullptr;
1359}
1360
1361+ (FlutterEngine*)engineForIdentifier:(int64_t)identifier {
1362 NSAssert([[NSThread currentThread] isMainThread], @"Must be called on the main thread.");
1363 return (__bridge FlutterEngine*)reinterpret_cast<void*>(identifier);
1364}
1365
1366- (void)setUpPlatformViewChannel {
1368 [FlutterMethodChannel methodChannelWithName:@"flutter/platform_views"
1369 binaryMessenger:self.binaryMessenger
1370 codec:[FlutterStandardMethodCodec sharedInstance]];
1371
1372 __weak FlutterEngine* weakSelf = self;
1373 [_platformViewsChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1374 [[weakSelf platformViewController] handleMethodCall:call result:result];
1375 }];
1376}
1377
1378- (void)setUpAccessibilityChannel {
1380 messageChannelWithName:@"flutter/accessibility"
1381 binaryMessenger:self.binaryMessenger
1383 __weak FlutterEngine* weakSelf = self;
1384 [_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
1385 [weakSelf handleAccessibilityEvent:message];
1386 }];
1387}
1388- (void)setUpNotificationCenterListeners {
1389 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1390 // macOS fires this private message when VoiceOver turns on or off.
1391 [center addObserver:self
1392 selector:@selector(onAccessibilityStatusChanged:)
1393 name:kEnhancedUserInterfaceNotification
1394 object:nil];
1395 [center addObserver:self
1396 selector:@selector(applicationWillTerminate:)
1397 name:NSApplicationWillTerminateNotification
1398 object:nil];
1399 [center addObserver:self
1400 selector:@selector(windowDidChangeScreen:)
1401 name:NSWindowDidChangeScreenNotification
1402 object:nil];
1403 [center addObserver:self
1404 selector:@selector(updateDisplayConfig:)
1405 name:NSApplicationDidChangeScreenParametersNotification
1406 object:nil];
1407}
1408
1409- (void)addInternalPlugins {
1410 __weak FlutterEngine* weakSelf = self;
1411 [FlutterMouseCursorPlugin registerWithRegistrar:[self registrarForPlugin:@"mousecursor"]
1412 delegate:self];
1413 [FlutterMenuPlugin registerWithRegistrar:[self registrarForPlugin:@"menu"]];
1414
1416 [FlutterBasicMessageChannel messageChannelWithName:kFlutterSettingsChannel
1417 binaryMessenger:self.binaryMessenger
1420 [FlutterMethodChannel methodChannelWithName:kFlutterPlatformChannel
1421 binaryMessenger:self.binaryMessenger
1422 codec:[FlutterJSONMethodCodec sharedInstance]];
1423 [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1424 [weakSelf handleMethodCall:call result:result];
1425 }];
1426
1428 [FlutterMethodChannel methodChannelWithName:@"flutter/screenshot"
1429 binaryMessenger:self.binaryMessenger
1430 codec:[FlutterStandardMethodCodec sharedInstance]];
1431 [_screenshotChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
1432 FlutterEngine* strongSelf = weakSelf;
1433 if (!strongSelf) {
1434 return result([FlutterError errorWithCode:@"invalid_state"
1435 message:@"Engine deallocated."
1436 details:nil]);
1437 }
1438
1439 FlutterViewController* viewController =
1440 [strongSelf viewControllerForIdentifier:flutter::kFlutterImplicitViewId];
1441 if (!viewController) {
1442 return result([FlutterError errorWithCode:@"failure"
1443 message:@"No view controller."
1444 details:nil]);
1445 }
1446
1447 NSArray<FlutterSurface*>* frontSurfaces =
1448 viewController.flutterView.surfaceManager.frontSurfaces;
1449 if (frontSurfaces.count == 0) {
1450 return result([FlutterError errorWithCode:@"failure"
1451 message:@"No front surfaces."
1452 details:nil]);
1453 }
1454
1455 // Use the first front surface (the main backing store).
1456 FlutterSurface* surface = frontSurfaces.firstObject;
1457 IOSurfaceRef ioSurface = surface.ioSurface;
1458
1459 size_t width = IOSurfaceGetWidth(ioSurface);
1460 size_t height = IOSurfaceGetHeight(ioSurface);
1461 size_t bytesPerRow = IOSurfaceGetBytesPerRow(ioSurface);
1462 size_t bytesPerElement = IOSurfaceGetBytesPerElement(ioSurface);
1463 uint32_t pixelFormat = (uint32_t)IOSurfaceGetPixelFormat(ioSurface);
1464
1465 NSString* formatString;
1466 switch (pixelFormat) {
1467 case kCVPixelFormatType_40ARGBLEWideGamut:
1468 formatString = @"MTLPixelFormatBGRA10_XR";
1469 break;
1470 case kCVPixelFormatType_32BGRA:
1471 formatString = @"MTLPixelFormatBGRA8Unorm";
1472 break;
1473 default:
1474 formatString = [NSString stringWithFormat:@"Unknown(%u)", pixelFormat];
1475 break;
1476 }
1477
1478 IOSurfaceLock(ioSurface, kIOSurfaceLockReadOnly, nil);
1479 void* baseAddress = IOSurfaceGetBaseAddress(ioSurface);
1480
1481 // Copy pixel data row by row into a tightly-packed buffer.
1482 size_t packedBytesPerRow = width * bytesPerElement;
1483 NSMutableData* packedData = [NSMutableData dataWithLength:packedBytesPerRow * height];
1484 uint8_t* dest = (uint8_t*)packedData.mutableBytes;
1485 for (size_t row = 0; row < height; row++) {
1486 memcpy(dest + row * packedBytesPerRow, (uint8_t*)baseAddress + row * bytesPerRow,
1487 packedBytesPerRow);
1488 }
1489
1490 IOSurfaceUnlock(ioSurface, kIOSurfaceLockReadOnly, nil);
1491
1492 return result(@[
1493 @(width),
1494 @(height),
1495 formatString,
1497 ]);
1498 }];
1499}
1500
1501- (void)didUpdateMouseCursor:(NSCursor*)cursor {
1502 // Mouse cursor plugin does not specify which view is responsible for changing the cursor,
1503 // so the reasonable assumption here is that cursor change is a result of a mouse movement
1504 // and thus the cursor will be paired with last Flutter view that reveived mouse event.
1505 [_lastViewWithPointerEvent didUpdateMouseCursor:cursor];
1506}
1507
1508- (void)applicationWillTerminate:(NSNotification*)notification {
1509 [self shutDownEngine];
1510}
1511
1512- (void)windowDidChangeScreen:(NSNotification*)notification {
1513 // Update window metric for all view controllers since the display_id has
1514 // changed.
1515 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1516 FlutterViewController* nextViewController;
1517 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1518 [self updateWindowMetricsForViewController:nextViewController];
1519 [nextViewController updateWideGamutForScreen];
1520 }
1521}
1522
1523- (void)onAccessibilityStatusChanged:(NSNotification*)notification {
1524 BOOL enabled = [notification.userInfo[kEnhancedUserInterfaceKey] boolValue];
1525 NSEnumerator* viewControllerEnumerator = [_viewControllers objectEnumerator];
1526 FlutterViewController* nextViewController;
1527 while ((nextViewController = [viewControllerEnumerator nextObject])) {
1528 [nextViewController onAccessibilityStatusChanged:enabled];
1529 }
1530
1531 self.semanticsEnabled = enabled;
1532}
1533- (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
1534 NSString* type = annotatedEvent[@"type"];
1535 if ([type isEqualToString:@"announce"]) {
1536 NSString* message = annotatedEvent[@"data"][@"message"];
1537 NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
1538 if (message == nil) {
1539 return;
1540 }
1541
1542 NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
1543 ? NSAccessibilityPriorityHigh
1544 : NSAccessibilityPriorityMedium;
1545
1546 [self announceAccessibilityMessage:message withPriority:priority];
1547 }
1548}
1549
1550- (void)announceAccessibilityMessage:(NSString*)message
1551 withPriority:(NSAccessibilityPriorityLevel)priority {
1552 NSAccessibilityPostNotificationWithUserInfo(
1553 [self viewControllerForIdentifier:kFlutterImplicitViewId].flutterView,
1554 NSAccessibilityAnnouncementRequestedNotification,
1555 @{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1556}
1557- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
1558 if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
1559 [[NSApplication sharedApplication] terminate:self];
1560 result(nil);
1561 } else if ([call.method isEqualToString:@"SystemSound.play"]) {
1562 [self playSystemSound:call.arguments];
1563 result(nil);
1564 } else if ([call.method isEqualToString:@"Clipboard.getData"]) {
1565 result([self getClipboardData:call.arguments]);
1566 } else if ([call.method isEqualToString:@"Clipboard.setData"]) {
1567 [self setClipboardData:call.arguments];
1568 result(nil);
1569 } else if ([call.method isEqualToString:@"Clipboard.hasStrings"]) {
1570 result(@{@"value" : @([self clipboardHasStrings])});
1571 } else if ([call.method isEqualToString:@"System.exitApplication"]) {
1572 if ([self terminationHandler] == nil) {
1573 // If the termination handler isn't set, then either we haven't
1574 // initialized it yet, or (more likely) the NSApp delegate isn't a
1575 // FlutterAppDelegate, so it can't cancel requests to exit. So, in that
1576 // case, just terminate when requested.
1577 [NSApp terminate:self];
1578 result(nil);
1579 } else {
1580 [[self terminationHandler] handleRequestAppExitMethodCall:call.arguments result:result];
1581 }
1582 } else if ([call.method isEqualToString:@"System.initializationComplete"]) {
1583 if ([self terminationHandler] != nil) {
1584 [self terminationHandler].acceptingRequests = YES;
1585 }
1586 result(nil);
1587 } else {
1589 }
1590}
1591
1592- (void)playSystemSound:(NSString*)soundType {
1593 if ([soundType isEqualToString:@"SystemSoundType.alert"]) {
1594 NSBeep();
1595 }
1596}
1597
1598- (NSDictionary*)getClipboardData:(NSString*)format {
1599 if ([format isEqualToString:@(kTextPlainFormat)]) {
1600 NSString* stringInPasteboard = [self.pasteboard stringForType:NSPasteboardTypeString];
1601 return stringInPasteboard == nil ? nil : @{@"text" : stringInPasteboard};
1602 }
1603 return nil;
1604}
1605
1606- (void)setClipboardData:(NSDictionary*)data {
1607 NSString* text = data[@"text"];
1608 [self.pasteboard clearContents];
1609 if (text && ![text isEqual:[NSNull null]]) {
1610 [self.pasteboard setString:text forType:NSPasteboardTypeString];
1611 }
1612}
1613
1614- (BOOL)clipboardHasStrings {
1615 return [self.pasteboard stringForType:NSPasteboardTypeString].length > 0;
1616}
1617
1618- (std::vector<std::string>)switches {
1620}
1621
1622#pragma mark - FlutterAppLifecycleDelegate
1623
1624- (void)setApplicationState:(flutter::AppLifecycleState)state {
1625 NSString* nextState =
1626 [[NSString alloc] initWithCString:flutter::AppLifecycleStateToString(state)];
1627 [self sendOnChannel:kFlutterLifecycleChannel
1628 message:[nextState dataUsingEncoding:NSUTF8StringEncoding]];
1629}
1630
1631/**
1632 * Called when the |FlutterAppDelegate| gets the applicationWillBecomeActive
1633 * notification.
1634 */
1635- (void)handleWillBecomeActive:(NSNotification*)notification {
1636 _active = YES;
1637 if (!_visible) {
1638 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1639 } else {
1640 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1641 }
1642}
1643
1644/**
1645 * Called when the |FlutterAppDelegate| gets the applicationWillResignActive
1646 * notification.
1647 */
1648- (void)handleWillResignActive:(NSNotification*)notification {
1649 _active = NO;
1650 if (!_visible) {
1651 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1652 } else {
1653 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1654 }
1655}
1656
1657/**
1658 * Called when the |FlutterAppDelegate| gets the applicationDidUnhide
1659 * notification.
1660 */
1661- (void)handleDidChangeOcclusionState:(NSNotification*)notification {
1662 NSApplicationOcclusionState occlusionState = [[NSApplication sharedApplication] occlusionState];
1663 if (occlusionState & NSApplicationOcclusionStateVisible) {
1664 _visible = YES;
1665 if (_active) {
1666 [self setApplicationState:flutter::AppLifecycleState::kResumed];
1667 } else {
1668 [self setApplicationState:flutter::AppLifecycleState::kInactive];
1669 }
1670 } else {
1671 _visible = NO;
1672 [self setApplicationState:flutter::AppLifecycleState::kHidden];
1673 }
1674}
1675
1676#pragma mark - FlutterBinaryMessenger
1677
1678- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
1679 [self sendOnChannel:channel message:message binaryReply:nil];
1680}
1681
1682- (void)sendOnChannel:(NSString*)channel
1683 message:(NSData* _Nullable)message
1684 binaryReply:(FlutterBinaryReply _Nullable)callback {
1685 FlutterPlatformMessageResponseHandle* response_handle = nullptr;
1686 if (callback) {
1687 struct Captures {
1688 FlutterBinaryReply reply;
1689 };
1690 auto captures = std::make_unique<Captures>();
1691 captures->reply = callback;
1692 auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) {
1693 auto captures = reinterpret_cast<Captures*>(user_data);
1694 NSData* reply_data = nil;
1695 if (data != nullptr && data_size > 0) {
1696 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size];
1697 }
1698 captures->reply(reply_data);
1699 delete captures;
1700 };
1701
1702 FlutterEngineResult create_result = _embedderAPI.PlatformMessageCreateResponseHandle(
1703 _engine, message_reply, captures.get(), &response_handle);
1704 if (create_result != kSuccess) {
1705 NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result);
1706 return;
1707 }
1708 captures.release();
1709 }
1710
1711 FlutterPlatformMessage platformMessage = {
1713 .channel = [channel UTF8String],
1714 .message = static_cast<const uint8_t*>(message.bytes),
1715 .message_size = message.length,
1716 .response_handle = response_handle,
1717 };
1718
1719 FlutterEngineResult message_result = _embedderAPI.SendPlatformMessage(_engine, &platformMessage);
1720 if (message_result != kSuccess) {
1721 NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel,
1722 message_result);
1723 }
1724
1725 if (response_handle != nullptr) {
1726 FlutterEngineResult release_result =
1727 _embedderAPI.PlatformMessageReleaseResponseHandle(_engine, response_handle);
1728 if (release_result != kSuccess) {
1729 NSLog(@"Failed to release the response handle (%d).", release_result);
1730 };
1731 }
1732}
1733
1734- (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(nonnull NSString*)channel
1735 binaryMessageHandler:
1736 (nullable FlutterBinaryMessageHandler)handler {
1738 _messengerHandlers[channel] =
1739 [[FlutterEngineHandlerInfo alloc] initWithConnection:@(_currentMessengerConnection)
1740 handler:[handler copy]];
1742}
1743
1744- (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection {
1745 // Find the _messengerHandlers that has the required connection, and record its
1746 // channel.
1747 NSString* foundChannel = nil;
1748 for (NSString* key in [_messengerHandlers allKeys]) {
1749 FlutterEngineHandlerInfo* handlerInfo = [_messengerHandlers objectForKey:key];
1750 if ([handlerInfo.connection isEqual:@(connection)]) {
1751 foundChannel = key;
1752 break;
1753 }
1754 }
1755 if (foundChannel) {
1756 [_messengerHandlers removeObjectForKey:foundChannel];
1757 }
1758}
1759
1760#pragma mark - FlutterPluginRegistry
1761
1762- (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName {
1763 id<FlutterPluginRegistrar> registrar = self.pluginRegistrars[pluginName];
1764 if (!registrar) {
1765 FlutterEngineRegistrar* registrarImpl =
1766 [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self];
1767 self.pluginRegistrars[pluginName] = registrarImpl;
1768 registrar = registrarImpl;
1769 }
1770 return registrar;
1771}
1772
1773- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginName {
1774 return self.pluginRegistrars[pluginName].publishedValue;
1775}
1776
1777#pragma mark - FlutterTextureRegistrar
1778
1779- (int64_t)registerTexture:(id<FlutterTexture>)texture {
1780 return [_renderer registerTexture:texture];
1781}
1782
1783- (BOOL)registerTextureWithID:(int64_t)textureId {
1784 return _embedderAPI.RegisterExternalTexture(_engine, textureId) == kSuccess;
1785}
1786
1787- (void)textureFrameAvailable:(int64_t)textureID {
1788 [_renderer textureFrameAvailable:textureID];
1789}
1790
1791- (BOOL)markTextureFrameAvailable:(int64_t)textureID {
1792 return _embedderAPI.MarkExternalTextureFrameAvailable(_engine, textureID) == kSuccess;
1793}
1794
1795- (void)unregisterTexture:(int64_t)textureID {
1796 [_renderer unregisterTexture:textureID];
1797}
1798
1799- (BOOL)unregisterTextureWithID:(int64_t)textureID {
1800 return _embedderAPI.UnregisterExternalTexture(_engine, textureID) == kSuccess;
1801}
1802
1803#pragma mark - Task runner integration
1804
1805- (void)postMainThreadTask:(FlutterTask)task targetTimeInNanoseconds:(uint64_t)targetTime {
1806 __weak FlutterEngine* weakSelf = self;
1807
1808 const auto engine_time = _embedderAPI.GetCurrentTime();
1809 [FlutterRunLoop.mainRunLoop
1810 performAfterDelay:(targetTime - (double)engine_time) / NSEC_PER_SEC
1811 block:^{
1812 FlutterEngine* self = weakSelf;
1813 if (self != nil && self->_engine != nil) {
1814 auto result = _embedderAPI.RunTask(self->_engine, &task);
1815 if (result != kSuccess) {
1816 NSLog(@"Could not post a task to the Flutter engine.");
1817 }
1818 }
1819 }];
1820}
1821
1822// Getter used by test harness, only exposed through the FlutterEngine(Test) category
1823- (flutter::FlutterCompositor*)macOSCompositor {
1824 return _macOSCompositor.get();
1825}
1826
1827#pragma mark - FlutterKeyboardManagerDelegate
1828
1829/**
1830 * Dispatches the given pointer event data to engine.
1831 */
1832- (void)sendKeyEvent:(const FlutterKeyEvent&)event
1833 callback:(FlutterKeyEventCallback)callback
1834 userData:(void*)userData {
1835 _embedderAPI.SendKeyEvent(_engine, &event, callback, userData);
1836}
1837
1838@end
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()
int32_t value
FlutterEngineResult FlutterEngineGetProcAddresses(FlutterEngineProcTable *table)
Gets the table of engine function pointers.
Definition embedder.cc:3737
#define FLUTTER_API_SYMBOL(symbol)
Definition embedder.h:67
@ kUnfocused
Specifies that a view does not have platform focus.
Definition embedder.h:1221
@ kFocused
Specifies that a view has platform focus.
Definition embedder.h:1224
@ kFlutterEngineAOTDataSourceTypeElfPath
Definition embedder.h:2466
@ kUndefined
Definition embedder.h:1205
void(* FlutterPlatformMessageCallback)(const FlutterPlatformMessage *, void *)
Definition embedder.h:1487
FlutterEngineResult
Definition embedder.h:72
@ kSuccess
Definition embedder.h:73
@ kFlutterEngineDisplaysUpdateTypeStartup
Definition embedder.h:2362
FlutterThreadPriority
Valid values for priority of Thread.
Definition embedder.h:376
@ kDisplay
Suitable for threads which generate data for the display.
Definition embedder.h:382
@ kRaster
Suitable for thread which raster data.
Definition embedder.h:384
FlutterSemanticsAction
Definition embedder.h:122
void(* FlutterKeyEventCallback)(bool, void *)
Definition embedder.h:1465
FlutterEngineResult(* FlutterEngineSendPlatformMessageResponseFnPtr)(FLUTTER_API_SYMBOL(FlutterEngine) engine, const FlutterPlatformMessageResponseHandle *handle, const uint8_t *data, size_t data_length)
Definition embedder.h:3661
#define FLUTTER_ENGINE_VERSION
Definition embedder.h:70
FlutterEngine engine
Definition main.cc:84
FlView * view
const char * message
const char FlTextDirection FlAssertiveness assertiveness
const gchar * channel
const gchar FlBinaryMessengerMessageHandler handler
const uint8_t uint32_t uint32_t GError ** error
uint32_t * target
G_BEGIN_DECLS FlutterViewId view_id
HWND(* FlutterPlatformViewFactory)(const FlutterPlatformViewCreationParameters *)
FlutterDesktopBinaryReply callback
#define FML_DCHECK(condition)
Definition logging.h:122
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)
FlutterBinaryMessageHandler handler
NSObject< FlutterBinaryMessenger > * binaryMessenger
instancetype errorWithCode:message:details:(NSString *code,[message] NSString *_Nullable message,[details] id _Nullable details)
void registerWithRegistrar:(nonnull id< FlutterPluginRegistrar > registrar)
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)
FlutterViewIdentifier viewIdentifier
void onAccessibilityStatusChanged:(BOOL enabled)
FlutterBinaryMessengerRelay * _binaryMessenger
FlutterViewController * viewController
std::u16string text
char ** argv
Definition library.h:9
int64_t FlutterViewIdentifier
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
BOOL _multiViewEnabled
FlutterCompositor _compositor
__weak FlutterView * _lastViewWithPointerEvent
FlutterKeyboardManager * _keyboardManager
FlutterWindowController * _windowController
FlutterTerminationCallback _terminator
constexpr char kTextPlainFormat[]
Clipboard plain text format.
__weak FlutterEngine * _flutterEngine
BOOL _visible
BOOL _active
FlutterBinaryMessengerRelay * _binaryMessenger
static NSString *const kEnhancedUserInterfaceKey
NSString *const kFlutterSettingsChannel
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterTerminationCallback)(id _Nullable sender)
constexpr int64_t kFlutterImplicitViewId
Definition constants.h:35
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36
std::vector< std::string > GetSwitchesFromEnvironment()
Definition ref_ptr.h:261
std::vector< FlutterEngineDisplay > * displays
instancetype sharedInstance()
impeller::ShaderType type
int32_t height
int32_t width
void * user_data
The |FlutterAddViewInfo.user_data|.
Definition embedder.h:1111
FlutterBackingStoreCreateCallback create_backing_store_callback
Definition embedder.h:2251
bool avoid_backing_store_cache
Definition embedder.h:2279
size_t struct_size
This size of this struct. Must be sizeof(FlutterCompositor).
Definition embedder.h:2235
FlutterPresentViewCallback present_view_callback
Definition embedder.h:2288
FlutterBackingStoreCollectCallback collect_backing_store_callback
Definition embedder.h:2256
size_t struct_size
The size of this struct. Must be sizeof(FlutterCustomTaskRunners).
Definition embedder.h:1929
FlutterEngineAOTDataSourceType type
Definition embedder.h:2472
const char * elf_path
Absolute path to an ELF library file.
Definition embedder.h:2475
size_t height
The height of the display, in physical pixels.
Definition embedder.h:2347
size_t struct_size
The size of this struct. Must be sizeof(FlutterEngineDisplay).
Definition embedder.h:2329
size_t width
The width of the display, in physical pixels.
Definition embedder.h:2344
FlutterEngineDisplayId display_id
Definition embedder.h:2331
Function-pointer-based versions of the APIs above.
Definition embedder.h:3746
const char * language_code
Definition embedder.h:2297
size_t struct_size
This size of this struct. Must be sizeof(FlutterLocale).
Definition embedder.h:2293
const char * script_code
Definition embedder.h:2307
const char * country_code
Definition embedder.h:2302
const char * variant_code
Definition embedder.h:2312
size_t struct_size
The size of this struct. Must be sizeof(FlutterPlatformMessage).
Definition embedder.h:1474
FlutterPlatformMessageCallback platform_message_callback
Definition embedder.h:2548
FlutterLogMessageCallback log_message_callback
Definition embedder.h:2740
FlutterViewFocusChangeRequestCallback view_focus_change_request_callback
Definition embedder.h:2798
VsyncCallback vsync_callback
Definition embedder.h:2645
const char * assets_path
Definition embedder.h:2500
OnPreEngineRestartCallback on_pre_engine_restart_callback
Definition embedder.h:2757
FlutterEngineAOTData aot_data
Definition embedder.h:2709
const char *const * dart_entrypoint_argv
Definition embedder.h:2732
size_t struct_size
The size of this struct. Must be sizeof(FlutterProjectArgs).
Definition embedder.h:2496
FlutterUpdateSemanticsCallback2 update_semantics_callback2
Definition embedder.h:2787
const char *const * command_line_argv
Definition embedder.h:2542
const char * icu_data_path
Definition embedder.h:2524
bool shutdown_dart_vm_when_done
Definition embedder.h:2678
const char * custom_dart_entrypoint
Definition embedder.h:2654
const FlutterCustomTaskRunners * custom_task_runners
Definition embedder.h:2659
int command_line_argc
The command line argument count used to initialize the project.
Definition embedder.h:2526
VoidCallback root_isolate_create_callback
Definition embedder.h:2583
const FlutterCompositor * compositor
Definition embedder.h:2694
FlutterRemoveViewCallback remove_view_callback
Definition embedder.h:1195
FlutterViewId view_id
Definition embedder.h:1178
A batch of updates to semantics nodes and custom actions.
Definition embedder.h:1834
size_t struct_size
The size of this struct. Must be sizeof(FlutterTaskRunnerDescription).
Definition embedder.h:1902
FlutterViewFocusState state
The focus state of the view.
Definition embedder.h:1260
size_t struct_size
The size of this struct. Must be sizeof(FlutterWindowMetricsEvent).
Definition embedder.h:1054
const uintptr_t id
int BOOL