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