未验证 提交 49445ce4 编写于 作者: S stuartmorgan 提交者: GitHub

FLEViewController/Engine API changes (#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 https://github.com/flutter/flutter/issues/31735
上级 2a79462f
......@@ -26,34 +26,46 @@ FLUTTER_EXPORT
/**
* Initializes an engine with the given viewController.
*
* @param viewController The view controller associated with this engine. If nil, the engine
* will be run headless.
* @param labelPrefix Currently unused; in the future, may be used for labelling threads
* as with the iOS FlutterEngine.
* @param project The project configuration. If nil, a default FLEDartProject will be used.
*/
- (nonnull instancetype)initWithViewController:(nullable FLEViewController*)viewController
project:(nullable FLEDartProject*)project
NS_DESIGNATED_INITIALIZER;
- (nonnull instancetype)initWithName:(nonnull NSString*)labelPrefix
project:(nullable FLEDartProject*)project;
/**
* Runs `main()` from this engine's project.
* Initializes an engine with the given viewController.
*
* @return YES if the engine launched successfully.
* @param labelPrefix Currently unused; in the future, may be used for labelling threads
* as with the iOS FlutterEngine.
* @param project The project configuration. If nil, a default FLEDartProject will be used.
*/
- (BOOL)run;
- (nonnull instancetype)initWithName:(nonnull NSString*)labelPrefix
project:(nullable FLEDartProject*)project
allowHeadlessExecution:(BOOL)allowHeadlessExecution NS_DESIGNATED_INITIALIZER;
- (nonnull instancetype)init NS_UNAVAILABLE;
/**
* The `FLEDartProject` associated with this engine. If nil, a default will be used for `run`.
* Runs a Dart program on an Isolate from the main Dart library (i.e. the library that
* contains `main()`).
*
* The first call to this method will create a new Isolate. Subsequent calls will return
* immediately.
*
* TODO(stuartmorgan): Remove this once FLEViewController takes the project as an initializer
* argument. Blocked on currently needing to create it from a XIB due to the view issues
* described in https://github.com/google/flutter-desktop-embedding/issues/10.
* @param entrypoint The name of a top-level function from the same Dart
* library that contains the app's main() function. If this is nil, it will
* default to `main()`. If it is not the app's main() function, that function
* must be decorated with `@pragma(vm:entry-point)` to ensure the method is not
* tree-shaken by the Dart compiler.
* @return YES if the call succeeds in creating and running a Flutter Engine instance; NO otherwise.
*/
@property(nonatomic, nullable) FLEDartProject* project;
- (BOOL)runWithEntrypoint:(nullable NSString*)entrypoint;
/**
* The `FLEViewController` associated with this engine, if any.
*/
@property(nonatomic, nullable, readonly, weak) FLEViewController* viewController;
@property(nonatomic, nullable, weak) FLEViewController* viewController;
/**
* The `FlutterBinaryMessenger` for communicating with this engine.
......
......@@ -41,12 +41,16 @@ FLUTTER_EXPORT
@property(nonatomic) FlutterMouseTrackingMode mouseTrackingMode;
/**
* Launches the Flutter engine with the provided project.
* Initializes a controller that will run the given project.
*
* @param project The project to run in this view controller. If nil, a default `FLEDartProject`
* will be used.
* @return YES if the engine launched successfully.
*/
- (BOOL)launchEngineWithProject:(nullable FLEDartProject*)project;
- (nonnull instancetype)initWithProject:(nullable FLEDartProject*)project NS_DESIGNATED_INITIALIZER;
- (nonnull instancetype)initWithNibName:(nullable NSString*)nibNameOrNil
bundle:(nullable NSBundle*)nibBundleOrNil
NS_DESIGNATED_INITIALIZER;
- (nonnull instancetype)initWithCoder:(nonnull NSCoder*)nibNameOrNil NS_DESIGNATED_INITIALIZER;
@end
......@@ -41,6 +41,11 @@
*/
- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message;
/**
* Shuts the Flutter engine if it is running.
*/
- (void)shutDownEngine;
@end
#pragma mark -
......@@ -120,20 +125,29 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
// The embedding-API-level engine object.
FlutterEngine _engine;
// The project being run by this engine.
FLEDartProject* _project;
// The context provided to the Flutter engine for resource loading.
NSOpenGLContext* _resourceContext;
// A mapping of channel names to the registered handlers for those channels.
NSMutableDictionary<NSString*, FlutterBinaryMessageHandler>* _messageHandlers;
// Whether the engine can continue running after the view controller is removed.
BOOL _allowHeadlessExecution;
}
- (instancetype)init {
return [self initWithViewController:nil project:nil];
- (instancetype)initWithName:(NSString*)labelPrefix project:(FLEDartProject*)project {
return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES];
}
- (instancetype)initWithViewController:(FLEViewController*)viewController
project:(FLEDartProject*)project {
- (instancetype)initWithName:(NSString*)labelPrefix
project:(FLEDartProject*)project
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_viewController = viewController;
_project = project ?: [[FLEDartProject alloc] init];
_messageHandlers = [[NSMutableDictionary alloc] init];
......@@ -141,17 +155,16 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
}
- (void)dealloc {
if (FlutterEngineShutdown(_engine) == kSuccess) {
_engine = NULL;
}
[self shutDownEngine];
}
- (void)setProject:(FLEDartProject*)project {
_project = project ?: [[FLEDartProject alloc] init];
}
- (BOOL)runWithEntrypoint:(NSString*)entrypoint {
if (self.running) {
return NO;
}
- (BOOL)run {
if (_engine != NULL) {
if (!_allowHeadlessExecution && !_viewController) {
NSLog(@"Attempted to run an engine with no view controller without headless mode enabled.");
return NO;
}
......@@ -175,6 +188,7 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
flutterArguments.command_line_argc = static_cast<int>(arguments.size());
flutterArguments.command_line_argv = &arguments[0];
flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage;
flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String;
FlutterEngineResult result = FlutterEngineRun(
FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine);
......@@ -182,9 +196,19 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
NSLog(@"Failed to start Flutter engine: error %d", result);
return NO;
}
[self updateWindowMetrics];
return YES;
}
- (void)setViewController:(FLEViewController*)controller {
_viewController = controller;
if (!controller && !_allowHeadlessExecution) {
[self shutDownEngine];
_resourceContext = nil;
}
[self updateWindowMetrics];
}
- (id<FlutterBinaryMessenger>)binaryMessenger {
// TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins
// keeping the engine alive.
......@@ -193,11 +217,33 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
#pragma mark - Framework-internal methods
- (void)updateWindowMetricsWithSize:(CGSize)size pixelRatio:(double)pixelRatio {
- (BOOL)running {
return _engine != nullptr;
}
- (NSOpenGLContext*)resourceContext {
if (!_resourceContext) {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADoubleBuffer, 0,
};
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
_resourceContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
}
return _resourceContext;
}
- (void)updateWindowMetrics {
if (!_engine) {
return;
}
NSView* view = _viewController.view;
CGSize scaledSize = [view convertRectToBacking:view.bounds].size;
double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
const FlutterWindowMetricsEvent event = {
.struct_size = sizeof(event),
.width = static_cast<size_t>(size.width),
.height = static_cast<size_t>(size.height),
.width = static_cast<size_t>(scaledSize.width),
.height = static_cast<size_t>(scaledSize.height),
.pixel_ratio = pixelRatio,
};
FlutterEngineSendWindowMetricsEvent(_engine, &event);
......@@ -237,7 +283,7 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
if (!_viewController.flutterView) {
return false;
}
[_viewController makeResourceContextCurrent];
[self.resourceContext makeCurrentContext];
return true;
}
......@@ -269,6 +315,19 @@ static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine*
}
}
/**
* Note: Called from dealloc. Should not use accessors or other methods.
*/
- (void)shutDownEngine {
if (_engine) {
FlutterEngineResult result = FlutterEngineShutdown(_engine);
if (result != kSuccess) {
NSLog(@"Failed to shut down Flutter engine: error %d", result);
}
}
_engine = nullptr;
}
#pragma mark - FlutterBinaryMessenger
- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message {
......
......@@ -4,17 +4,27 @@
#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEEngine.h"
#import <Cocoa/Cocoa.h>
#import "flutter/shell/platform/embedder/embedder.h"
@interface FLEEngine ()
/**
* Informs the engine that the display region's size has changed.
*
* @param size The size of the display, in pixels.
* @param pixelRatio The number of pixels per screen coordinate.
* True if the engine is currently running.
*/
@property(nonatomic, readonly) BOOL running;
/**
* The resource context used by the engine for texture uploads. FlutterViews associated with this
* engine should be created to share with this context.
*/
@property(nonatomic, readonly, nullable) NSOpenGLContext* resourceContext;
/**
* Informs the engine that the associated view controller's view size has changed.
*/
- (void)updateWindowMetricsWithSize:(CGSize)size pixelRatio:(double)pixelRatio;
- (void)updateWindowMetrics;
/**
* Dispatches the given pointer event data to engine.
......
......@@ -84,6 +84,11 @@ struct MouseState {
*/
@property(nonatomic) MouseState mouseState;
/**
* Starts running |engine|, including any initial setup.
*/
- (BOOL)launchEngine;
/**
* Updates |trackingArea| for the current tracking settings, creating it with
* the correct mode if tracking is enabled, or removing it if not.
......@@ -95,11 +100,6 @@ struct MouseState {
*/
- (void)addInternalPlugins;
/**
* Creates the OpenGL context used as the resource context by the engine.
*/
- (void)createResourceContext;
/**
* Calls dispatchMouseEvent:phase: with a phase determined by self.mouseState.
*
......@@ -149,8 +149,8 @@ struct MouseState {
#pragma mark - FLEViewController implementation.
@implementation FLEViewController {
// The additional context provided to the Flutter engine for resource loading.
NSOpenGLContext* _resourceContext;
// The project to run in this controller's engine.
FLEDartProject* _project;
// The plugin used to handle text input. This is not an FlutterPlugin, so must be owned
// separately.
......@@ -173,29 +173,46 @@ struct MouseState {
* Performs initialization that's common between the different init paths.
*/
static void CommonInit(FLEViewController* controller) {
controller->_engine = [[FLEEngine alloc] initWithViewController:controller project:nil];
controller->_engine = [[FLEEngine alloc] initWithName:@"io.flutter"
project:controller->_project
allowHeadlessExecution:NO];
controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init];
controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow;
}
- (instancetype)initWithCoder:(NSCoder*)coder {
self = [super initWithCoder:coder];
if (self != nil) {
CommonInit(self);
}
NSAssert(self, @"Super init cannot be nil");
CommonInit(self);
return self;
}
- (instancetype)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self != nil) {
CommonInit(self);
}
NSAssert(self, @"Super init cannot be nil");
CommonInit(self);
return self;
}
- (instancetype)initWithProject:(nullable FLEDartProject*)project {
self = [super initWithNibName:nil bundle:nil];
NSAssert(self, @"Super init cannot be nil");
_project = project;
CommonInit(self);
return self;
}
- (void)loadView {
FlutterView* flutterView = [[FlutterView alloc] initWithReshapeListener:self];
NSOpenGLContext* resourceContext = _engine.resourceContext;
if (!resourceContext) {
NSLog(@"Unable to create FlutterView; no resource context available.");
return;
}
FlutterView* flutterView = [[FlutterView alloc] initWithShareContext:resourceContext
reshapeListener:self];
self.view = flutterView;
}
......@@ -203,6 +220,17 @@ static void CommonInit(FLEViewController* controller) {
[self configureTrackingArea];
}
- (void)viewWillAppear {
[super viewWillAppear];
if (!_engine.running) {
[self launchEngine];
}
}
- (void)dealloc {
_engine.viewController = nil;
}
#pragma mark - Public methods
- (void)setMouseTrackingMode:(FlutterMouseTrackingMode)mode {
......@@ -213,25 +241,6 @@ static void CommonInit(FLEViewController* controller) {
[self configureTrackingArea];
}
- (BOOL)launchEngineWithProject:(nullable FLEDartProject*)project {
// Set up the resource context. This is done here rather than in viewDidLoad as there's no
// guarantee that viewDidLoad will be called before the engine is started, and the context must
// be valid by that point.
[self createResourceContext];
// Register internal plugins before starting the engine.
[self addInternalPlugins];
_engine.project = project;
if (![_engine run]) {
return NO;
}
// Send the initial user settings such as brightness and text scale factor
// to the engine.
[self sendInitialSettings];
return YES;
}
#pragma mark - Framework-internal methods
- (FlutterView*)flutterView {
......@@ -246,12 +255,22 @@ static void CommonInit(FLEViewController* controller) {
[self.additionalKeyResponders removeObject:responder];
}
- (void)makeResourceContextCurrent {
[_resourceContext makeCurrentContext];
}
#pragma mark - Private methods
- (BOOL)launchEngine {
// Register internal plugins before starting the engine.
[self addInternalPlugins];
_engine.viewController = self;
if (![_engine runWithEntrypoint:nil]) {
return NO;
}
// Send the initial user settings such as brightness and text scale factor
// to the engine.
[self sendInitialSettings];
return YES;
}
- (void)configureTrackingArea {
if (_mouseTrackingMode != FlutterMouseTrackingModeNone && self.view) {
NSTrackingAreaOptions options =
......@@ -301,12 +320,6 @@ static void CommonInit(FLEViewController* controller) {
}];
}
- (void)createResourceContext {
NSOpenGLContext* viewContext = ((NSOpenGLView*)self.view).openGLContext;
_resourceContext = [[NSOpenGLContext alloc] initWithFormat:viewContext.pixelFormat
shareContext:viewContext];
}
- (void)dispatchMouseEvent:(nonnull NSEvent*)event {
FlutterPointerPhase phase = _mouseState.buttons == 0
? (_mouseState.flutter_state_is_down ? kUp : kHover)
......@@ -455,9 +468,7 @@ static void CommonInit(FLEViewController* controller) {
* Responds to view reshape by notifying the engine of the change in dimensions.
*/
- (void)viewDidReshape:(NSView*)view {
CGSize scaledSize = [view convertRectToBacking:view.bounds].size;
double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width;
[_engine updateWindowMetricsWithSize:scaledSize pixelRatio:pixelRatio];
[_engine updateWindowMetrics];
}
#pragma mark - FlutterPluginRegistry
......
......@@ -22,10 +22,4 @@
*/
- (void)removeKeyResponder:(nonnull NSResponder*)responder;
/**
* Called when the engine wants to make the resource context current. This must be a context
* that is in the same share group as this controller's view.
*/
- (void)makeResourceContextCurrent;
@end
......@@ -21,11 +21,13 @@
@interface FlutterView : NSOpenGLView
- (nullable instancetype)initWithFrame:(NSRect)frame
shareContext:(nonnull NSOpenGLContext*)shareContext
reshapeListener:(nonnull id<FlutterViewReshapeListener>)reshapeListener
NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithReshapeListener:
(nonnull id<FlutterViewReshapeListener>)reshapeListener;
- (nullable instancetype)initWithShareContext:(nonnull NSOpenGLContext*)shareContext
reshapeListener:
(nonnull id<FlutterViewReshapeListener>)reshapeListener;
- (nullable instancetype)initWithFrame:(NSRect)frameRect
pixelFormat:(nullable NSOpenGLPixelFormat*)format NS_UNAVAILABLE;
......
......@@ -8,18 +8,18 @@
__weak id<FlutterViewReshapeListener> _reshapeListener;
}
- (instancetype)initWithReshapeListener:(id<FlutterViewReshapeListener>)reshapeListener {
return [self initWithFrame:NSZeroRect reshapeListener:reshapeListener];
- (instancetype)initWithShareContext:(NSOpenGLContext*)shareContext
reshapeListener:(id<FlutterViewReshapeListener>)reshapeListener {
return [self initWithFrame:NSZeroRect shareContext:shareContext reshapeListener:reshapeListener];
}
- (instancetype)initWithFrame:(NSRect)frame
shareContext:(NSOpenGLContext*)shareContext
reshapeListener:(id<FlutterViewReshapeListener>)reshapeListener {
NSOpenGLPixelFormatAttribute attributes[] = {
NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADoubleBuffer, 0,
};
NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes];
self = [super initWithFrame:frame pixelFormat:pixelFormat];
self = [super initWithFrame:frame];
if (self) {
self.openGLContext = [[NSOpenGLContext alloc] initWithFormat:shareContext.pixelFormat
shareContext:shareContext];
_reshapeListener = reshapeListener;
self.wantsBestResolutionOpenGLSurface = YES;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册