Skip to content

Commit 49445ce

Browse files
FLEViewController/Engine API changes (flutter#9750)
Updates the way FLEViewController and FLEEngine interact, making their APIs much more closely aligned with the iOS versions of the classes. As part of the change, removes the need for an explicit launch call on FLEViewController. Also adds entrypoint support when running an engine directly, matching iOS. Breaking change for macOS runners. Part of flutter#31735
1 parent 2a79462 commit 49445ce

File tree

8 files changed

+193
-101
lines changed

8 files changed

+193
-101
lines changed

shell/platform/darwin/macos/framework/Headers/FLEEngine.h

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,34 +26,46 @@ FLUTTER_EXPORT
2626
/**
2727
* Initializes an engine with the given viewController.
2828
*
29-
* @param viewController The view controller associated with this engine. If nil, the engine
30-
* will be run headless.
29+
* @param labelPrefix Currently unused; in the future, may be used for labelling threads
30+
* as with the iOS FlutterEngine.
3131
* @param project The project configuration. If nil, a default FLEDartProject will be used.
3232
*/
33-
- (nonnull instancetype)initWithViewController:(nullable FLEViewController*)viewController
34-
project:(nullable FLEDartProject*)project
35-
NS_DESIGNATED_INITIALIZER;
33+
- (nonnull instancetype)initWithName:(nonnull NSString*)labelPrefix
34+
project:(nullable FLEDartProject*)project;
3635

3736
/**
38-
* Runs `main()` from this engine's project.
37+
* Initializes an engine with the given viewController.
3938
*
40-
* @return YES if the engine launched successfully.
39+
* @param labelPrefix Currently unused; in the future, may be used for labelling threads
40+
* as with the iOS FlutterEngine.
41+
* @param project The project configuration. If nil, a default FLEDartProject will be used.
4142
*/
42-
- (BOOL)run;
43+
- (nonnull instancetype)initWithName:(nonnull NSString*)labelPrefix
44+
project:(nullable FLEDartProject*)project
45+
allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER;
46+
47+
- (nonnull instancetype)init NS_UNAVAILABLE;
4348

4449
/**
45-
* The `FLEDartProject` associated with this engine. If nil, a default will be used for `run`.
50+
* Runs a Dart program on an Isolate from the main Dart library (i.e. the library that
51+
* contains `main()`).
52+
*
53+
* The first call to this method will create a new Isolate. Subsequent calls will return
54+
* immediately.
4655
*
47-
* TODO(stuartmorgan): Remove this once FLEViewController takes the project as an initializer
48-
* argument. Blocked on currently needing to create it from a XIB due to the view issues
49-
* described in https://github.com/google/flutter-desktop-embedding/issues/10.
56+
* @param entrypoint The name of a top-level function from the same Dart
57+
* library that contains the app's main() function. If this is nil, it will
58+
* default to `main()`. If it is not the app's main() function, that function
59+
* must be decorated with `@pragma(vm:entry-point)` to ensure the method is not
60+
* tree-shaken by the Dart compiler.
61+
* @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise.
5062
*/
51-
@property(nonatomic, nullable) FLEDartProject* project;
63+
- (BOOL)runWithEntrypoint:(nullable NSString*)entrypoint;
5264

5365
/**
5466
* The `FLEViewController` associated with this engine, if any.
5567
*/
56-
@property(nonatomic, nullable, readonly, weak) FLEViewController* viewController;
68+
@property(nonatomic, nullable, weak) FLEViewController* viewController;
5769

5870
/**
5971
* The `FlutterBinaryMessenger` for communicating with this engine.

shell/platform/darwin/macos/framework/Headers/FLEViewController.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,16 @@ FLUTTER_EXPORT
4141
@property(nonatomic) FlutterMouseTrackingMode mouseTrackingMode;
4242

4343
/**
44-
* Launches the Flutter engine with the provided project.
44+
* Initializes a controller that will run the given project.
4545
*
4646
* @param project The project to run in this view controller. If nil, a default `FLEDartProject`
4747
* will be used.
48-
* @return YES if the engine launched successfully.
4948
*/
50-
- (BOOL)launchEngineWithProject:(nullable FLEDartProject*)project;
49+
- (nonnull instancetype)initWithProject:(nullable FLEDartProject*)project NS_DESIGNATED_INITIALIZER;
50+
51+
- (nonnull instancetype)initWithNibName:(nullable NSString*)nibNameOrNil
52+
bundle:(nullable NSBundle*)nibBundleOrNil
53+
NS_DESIGNATED_INITIALIZER;
54+
- (nonnull instancetype)initWithCoder:(nonnull NSCoder*)nibNameOrNil NS_DESIGNATED_INITIALIZER;
5155

5256
@end

shell/platform/darwin/macos/framework/Source/FLEEngine.mm

Lines changed: 76 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ - (bool)engineCallbackOnMakeResourceCurrent;
4141
*/
4242
- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
4343

44+
/**
45+
* Shuts the Flutter engine if it is running.
46+
*/
47+
- (void)shutDownEngine;
48+
4449
@end
4550

4651
#pragma mark -
@@ -120,38 +125,46 @@ @implementation FLEEngine {
120125
// The embedding-API-level engine object.
121126
FlutterEngine _engine;
122127

128+
// The project being run by this engine.
129+
FLEDartProject* _project;
130+
131+
// The context provided to the Flutter engine for resource loading.
132+
NSOpenGLContext* _resourceContext;
133+
123134
// A mapping of channel names to the registered handlers for those channels.
124135
NSMutableDictionary<NSString*, FlutterBinaryMessageHandler>* _messageHandlers;
136+
137+
// Whether the engine can continue running after the view controller is removed.
138+
BOOL _allowHeadlessExecution;
125139
}
126140

127-
- (instancetype)init {
128-
return [self initWithViewController:nil project:nil];
141+
- (instancetype)initWithName:(NSString*)labelPrefix project:(FLEDartProject*)project {
142+
return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
129143
}
130144

131-
- (instancetype)initWithViewController:(FLEViewController*)viewController
132-
project:(FLEDartProject*)project {
145+
- (instancetype)initWithName:(NSString*)labelPrefix
146+
project:(FLEDartProject*)project
147+
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
133148
self = [super init];
134149
NSAssert(self, @"Super init cannot be nil");
135150

136-
_viewController = viewController;
137151
_project = project ?: [[FLEDartProject alloc] init];
138152
_messageHandlers = [[NSMutableDictionary alloc] init];
139153

140154
return self;
141155
}
142156

143157
- (void)dealloc {
144-
if (FlutterEngineShutdown(_engine) == kSuccess) {
145-
_engine = NULL;
146-
}
158+
[self shutDownEngine];
147159
}
148160

149-
- (void)setProject:(FLEDartProject*)project {
150-
_project = project ?: [[FLEDartProject alloc] init];
151-
}
161+
- (BOOL)runWithEntrypoint:(NSString*)entrypoint {
162+
if (self.running) {
163+
return NO;
164+
}
152165

153-
- (BOOL)run {
154-
if (_engine != NULL) {
166+
if (!_allowHeadlessExecution && !_viewController) {
167+
NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
155168
return NO;
156169
}
157170

@@ -175,16 +188,27 @@ - (BOOL)run {
175188
flutterArguments.command_line_argc = static_cast<int>(arguments.size());
176189
flutterArguments.command_line_argv = &arguments[0];
177190
flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
191+
flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
178192

179193
FlutterEngineResult result = FlutterEngineRun(
180194
FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
181195
if (result != kSuccess) {
182196
NSLog(@"Failed to start Flutter engine: error %d", result);
183197
return NO;
184198
}
199+
[self updateWindowMetrics];
185200
return YES;
186201
}
187202

203+
- (void)setViewController:(FLEViewController*)controller {
204+
_viewController = controller;
205+
if (!controller && !_allowHeadlessExecution) {
206+
[self shutDownEngine];
207+
_resourceContext = nil;
208+
}
209+
[self updateWindowMetrics];
210+
}
211+
188212
- (id<FlutterBinaryMessenger>)binaryMessenger {
189213
// TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins
190214
// keeping the engine alive.
@@ -193,11 +217,33 @@ - (BOOL)run {
193217

194218
#pragma mark - Framework-internal methods
195219

196-
- (void)updateWindowMetricsWithSize:(CGSize)size pixelRatio:(double)pixelRatio {
220+
- (BOOL)running {
221+
return _engine != nullptr;
222+
}
223+
224+
- (NSOpenGLContext*)resourceContext {
225+
if (!_resourceContext) {
226+
NSOpenGLPixelFormatAttribute attributes[] = {
227+
NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADoubleBuffer, 0,
228+
};
229+
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
230+
_resourceContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
231+
}
232+
return _resourceContext;
233+
}
234+
235+
- (void)updateWindowMetrics {
236+
if (!_engine) {
237+
return;
238+
}
239+
NSView* view = _viewController.view;
240+
CGSize scaledSize = [view convertRectToBacking:view.bounds].size;
241+
double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
242+
197243
const FlutterWindowMetricsEvent event = {
198244
.struct_size = sizeof(event),
199-
.width = static_cast<size_t>(size.width),
200-
.height = static_cast<size_t>(size.height),
245+
.width = static_cast<size_t>(scaledSize.width),
246+
.height = static_cast<size_t>(scaledSize.height),
201247
.pixel_ratio = pixelRatio,
202248
};
203249
FlutterEngineSendWindowMetricsEvent(_engine, &event);
@@ -237,7 +283,7 @@ - (bool)engineCallbackOnMakeResourceCurrent {
237283
if (!_viewController.flutterView) {
238284
return false;
239285
}
240-
[_viewController makeResourceContextCurrent];
286+
[self.resourceContext makeCurrentContext];
241287
return true;
242288
}
243289

@@ -269,6 +315,19 @@ - (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message {
269315
}
270316
}
271317

318+
/**
319+
* Note: Called from dealloc. Should not use accessors or other methods.
320+
*/
321+
- (void)shutDownEngine {
322+
if (_engine) {
323+
FlutterEngineResult result = FlutterEngineShutdown(_engine);
324+
if (result != kSuccess) {
325+
NSLog(@"Failed to shut down Flutter engine: error %d", result);
326+
}
327+
}
328+
_engine = nullptr;
329+
}
330+
272331
#pragma mark - FlutterBinaryMessenger
273332

274333
- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {

shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,27 @@
44

55
#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEEngine.h"
66

7+
#import <Cocoa/Cocoa.h>
8+
79
#import "flutter/shell/platform/embedder/embedder.h"
810

911
@interface FLEEngine ()
1012

1113
/**
12-
* Informs the engine that the display region's size has changed.
13-
*
14-
* @param size The size of the display, in pixels.
15-
* @param pixelRatio The number of pixels per screen coordinate.
14+
* True if the engine is currently running.
15+
*/
16+
@property(nonatomic, readonly) BOOL running;
17+
18+
/**
19+
* The resource context used by the engine for texture uploads. FlutterViews associated with this
20+
* engine should be created to share with this context.
21+
*/
22+
@property(nonatomic, readonly, nullable) NSOpenGLContext* resourceContext;
23+
24+
/**
25+
* Informs the engine that the associated view controller's view size has changed.
1626
*/
17-
- (void)updateWindowMetricsWithSize:(CGSize)size pixelRatio:(double)pixelRatio;
27+
- (void)updateWindowMetrics;
1828

1929
/**
2030
* Dispatches the given pointer event data to engine.

0 commit comments

Comments
 (0)