From 55a401a8c6cd7d1b1af77c78d812870bfa9ee891 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 2 Jul 2019 16:33:32 -0700 Subject: [PATCH 1/4] Begin separating macOS engine from view controller Creates an initial, minimal FLEEngine that parallels the iOS FlutterEngine, and moves engine launch, plugin registration, and binary messenger functionality from FLEViewController to there. Uses the FLE prefix since the current APIs will change to better match FlutterEngine; the current APIs are intended to minimize conceptual changes relative to the current FLEViewController APIs since it's already a substantial change. Further changes will move channels from FLEViewController to FLEEngine, and futher align the APIs and functionality with the iOS version. --- shell/platform/darwin/macos/BUILD.gn | 3 + .../macos/framework/Headers/FLEEngine.h | 59 ++++ .../framework/Headers/FLEViewController.h | 37 +-- .../macos/framework/Source/FLEEngine.mm | 304 +++++++++++++++++ .../framework/Source/FLEEngine_Internal.h | 24 ++ .../framework/Source/FLETextInputPlugin.mm | 2 +- .../framework/Source/FLEViewController.mm | 312 ++---------------- .../Source/FLEViewController_Internal.h | 6 + 8 files changed, 441 insertions(+), 306 deletions(-) create mode 100644 shell/platform/darwin/macos/framework/Headers/FLEEngine.h create mode 100644 shell/platform/darwin/macos/framework/Source/FLEEngine.mm create mode 100644 shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h diff --git a/shell/platform/darwin/macos/BUILD.gn b/shell/platform/darwin/macos/BUILD.gn index dceb7419b9dd0..f5bb498a0fb97 100644 --- a/shell/platform/darwin/macos/BUILD.gn +++ b/shell/platform/darwin/macos/BUILD.gn @@ -33,6 +33,7 @@ _flutter_framework_headers = [ "framework/Headers/FlutterMacOS.h", "framework/Headers/FlutterPluginMacOS.h", "framework/Headers/FlutterPluginRegistrarMacOS.h", + "framework/Headers/FLEEngine.h", "framework/Headers/FLEOpenGLContextHandling.h", "framework/Headers/FLEReshapeListener.h", "framework/Headers/FLEView.h", @@ -48,6 +49,8 @@ shared_library("create_flutter_framework_dylib") { output_name = "$_flutter_framework_name" sources = [ + "framework/Source/FLEEngine.mm", + "framework/Source/FLEEngine_Internal.h", "framework/Source/FLETextInputModel.h", "framework/Source/FLETextInputModel.mm", "framework/Source/FLETextInputPlugin.h", diff --git a/shell/platform/darwin/macos/framework/Headers/FLEEngine.h b/shell/platform/darwin/macos/framework/Headers/FLEEngine.h new file mode 100644 index 0000000000000..17053caa8ece1 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Headers/FLEEngine.h @@ -0,0 +1,59 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_FLEENGINE_H_ +#define FLUTTER_FLEENGINE_H_ + +#import + +#include "FlutterBinaryMessenger.h" +#include "FlutterMacros.h" +#include "FlutterPluginRegistrarMacOS.h" + +@class FLEViewController; + +/** + * Coordinates a single instance of execution of a Flutter engine. + * + * TODO(stuartmorgan): Finish aligning this (and ideally merging) with FlutterEngine. Currently + * this is largely usable only as an implementation detail of FLEViewController. + */ +FLUTTER_EXPORT +@interface FLEEngine : NSObject + +/** + * Initializes an engine with the given viewController. + * + * @param viewController The view controller associated with this engine. If nil, the engine + * will be run headless. + */ +- (nonnull instancetype)initWithViewController:(nullable FLEViewController*)viewController; + +/** + * Launches the Flutter engine with the provided configuration. + * + * @param assets The path to the flutter_assets folder for the Flutter application to be run. + * @param arguments Arguments to pass to the Flutter engine. See + * https://github.com/flutter/engine/blob/master/shell/common/switches.h + * for details. Not all arguments will apply to embedding mode. + * Note: This API layer will abstract arguments in the future, instead of + * providing a direct passthrough. + * @return YES if the engine launched successfully. + */ +- (BOOL)launchEngineWithAssetsPath:(nonnull NSURL*)assets + commandLineArguments:(nullable NSArray*)arguments; + +/** + * The `FLEViewController` associated with this engine, if any. + */ +@property(nonatomic, nullable, readonly, weak) FLEViewController* viewController; + +/** + * The `FlutterBinaryMessenger` for communicating with this engine. + */ +@property(nonatomic, nonnull, readonly) id binaryMessenger; + +@end + +#endif // FLUTTER_FLEENGINE_H_ diff --git a/shell/platform/darwin/macos/framework/Headers/FLEViewController.h b/shell/platform/darwin/macos/framework/Headers/FLEViewController.h index 54d05dc3d009a..18b5731f98b0f 100644 --- a/shell/platform/darwin/macos/framework/Headers/FLEViewController.h +++ b/shell/platform/darwin/macos/framework/Headers/FLEViewController.h @@ -4,9 +4,9 @@ #import +#import "FLEEngine.h" #import "FLEOpenGLContextHandling.h" #import "FLEReshapeListener.h" -#import "FlutterBinaryMessenger.h" #import "FlutterMacros.h" #import "FlutterPluginRegistrarMacOS.h" @@ -29,15 +29,19 @@ typedef NS_ENUM(NSInteger, FlutterMouseTrackingMode) { * Flutter engine in non-interactive mode, or with a drawable Flutter canvas. */ FLUTTER_EXPORT -@interface FLEViewController - : NSViewController +@interface FLEViewController : NSViewController /** - * The view this controller manages when launched in interactive mode (headless set to false). Must - * be capable of handling text input events, and the OpenGL context handling protocols. + * The view this controller manages. Must be capable of handling text input events, and the OpenGL + * context handling protocols. */ @property(nullable) NSView* view; +/** + * The Flutter engine associated with this view controller. + */ +@property(nonatomic, nonnull, readonly) FLEEngine* engine; + /** * The style of mouse tracking to use for the view. Defaults to * FlutterMouseTrackingModeInKeyWindow. @@ -49,28 +53,13 @@ FLUTTER_EXPORT * * @param assets The path to the flutter_assets folder for the Flutter application to be run. * @param arguments Arguments to pass to the Flutter engine. See - * https://github.com/flutter/engine/blob/master/shell/common/switches.h - * for details. Not all arguments will apply to embedding mode. - * Note: This API layer will likely abstract arguments in the future, instead of - * providing a direct passthrough. + * https://github.com/flutter/engine/blob/master/shell/common/switches.h + * for details. Not all arguments will apply to embedding mode. + * Note: This API layer will abstract in the future, instead of providing a direct + * passthrough. * @return YES if the engine launched successfully. */ - (BOOL)launchEngineWithAssetsPath:(nonnull NSURL*)assets commandLineArguments:(nullable NSArray*)arguments; -/** - * Launches the Flutter engine in headless mode with the provided configuration. In headless mode, - * this controller's view should not be displayed. - * - * See launcheEngineWithAssetsPath:commandLineArguments: for details. - */ -- (BOOL)launchHeadlessEngineWithAssetsPath:(nonnull NSURL*)assets - commandLineArguments:(nullable NSArray*)arguments; - -/** - * The `FlutterBinaryMessenger` associated with this FLEViewController (used for communicating - * with channels). - */ -@property(nonatomic, readonly) NSObject* _Nonnull binaryMessenger; - @end diff --git a/shell/platform/darwin/macos/framework/Source/FLEEngine.mm b/shell/platform/darwin/macos/framework/Source/FLEEngine.mm new file mode 100644 index 0000000000000..7e26e5fc2b91a --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLEEngine.mm @@ -0,0 +1,304 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEEngine.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h" + +#include + +#import "flutter/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h" +#import "flutter/shell/platform/embedder/embedder.h" + +static NSString* const kICUBundlePath = @"icudtl.dat"; + +/** + * Private interface declaration for FLEEngine. + */ +@interface FLEEngine () + +/** + * Called by the engine to make the context the engine should draw into current. + */ +- (bool)engineCallbackOnMakeCurrent; + +/** + * Called by the engine to clear the context the engine should draw into. + */ +- (bool)engineCallbackOnClearCurrent; + +/** + * Called by the engine when the context's buffers should be swapped. + */ +- (bool)engineCallbackOnPresent; + +/** + * Makes the resource context the current context. + */ +- (bool)engineCallbackOnMakeResourceCurrent; + +/** + * Handles a platform message from the engine. + */ +- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message; + +@end + +#pragma mark - + +/** + * `FlutterPluginRegistrar` implementation handling a single plugin. + */ +@interface FlutterEngineRegistrar : NSObject +- (instancetype)initWithPlugin:(nonnull NSString*)pluginKey + flutterEngine:(nonnull FLEEngine*)flutterEngine; +@end + +@implementation FlutterEngineRegistrar { + NSString* _pluginKey; + FLEEngine* _flutterEngine; +} + +- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FLEEngine*)flutterEngine { + self = [super init]; + if (self) { + _pluginKey = [pluginKey copy]; + _flutterEngine = flutterEngine; + } + return self; +} + +#pragma mark - FlutterPluginRegistrar + +- (id)messenger { + return _flutterEngine.binaryMessenger; +} + +- (NSView*)view { + return _flutterEngine.viewController.view; +} + +- (void)addMethodCallDelegate:(nonnull id)delegate + channel:(nonnull FlutterMethodChannel*)channel { + [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { + [delegate handleMethodCall:call result:result]; + }]; +} + +@end + +// Callbacks provided to the engine. See the called methods for documentation. +#pragma mark - Static methods provided to engine configuration + +static bool OnMakeCurrent(FLEEngine* engine) { + return [engine engineCallbackOnMakeCurrent]; +} + +static bool OnClearCurrent(FLEEngine* engine) { + return [engine engineCallbackOnClearCurrent]; +} + +static bool OnPresent(FLEEngine* engine) { + return [engine engineCallbackOnPresent]; +} + +static uint32_t OnFBO(FLEEngine* engine) { + // There is currently no case where a different FBO is used, so no need to forward. + return 0; +} + +static bool OnMakeResourceCurrent(FLEEngine* engine) { + return [engine engineCallbackOnMakeResourceCurrent]; +} + +static void OnPlatformMessage(const FlutterPlatformMessage* message, FLEEngine* engine) { + [engine engineCallbackOnPlatformMessage:message]; +} + +#pragma mark - FLEEngine implementation + +@implementation FLEEngine { + // The embedding-API-level engine object. + FlutterEngine _engine; + + // A mapping of channel names to the registered handlers for those channels. + NSMutableDictionary* _messageHandlers; +} + +- (instancetype)init { + return [self initWithViewController:nil]; +} + +- (instancetype)initWithViewController:(FLEViewController*)viewController { + self = [super init]; + if (self != nil) { + _viewController = viewController; + _messageHandlers = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)dealloc { + if (FlutterEngineShutdown(_engine) == kSuccess) { + _engine = NULL; + } +} + +- (BOOL)launchEngineWithAssetsPath:(NSURL*)assets + commandLineArguments:(NSArray*)arguments { + if (_engine != NULL) { + return NO; + } + + const FlutterRendererConfig rendererConfig = { + .type = kOpenGL, + .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), + .open_gl.make_current = (BoolCallback)OnMakeCurrent, + .open_gl.clear_current = (BoolCallback)OnClearCurrent, + .open_gl.present = (BoolCallback)OnPresent, + .open_gl.fbo_callback = (UIntCallback)OnFBO, + .open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent, + }; + + // TODO(stuartmorgan): Move internal channel registration from FLEViewController to here. + + // FlutterProjectArgs is expecting a full argv, so when processing it for flags the first + // item is treated as the executable and ignored. Add a dummy value so that all provided arguments + // are used. + std::vector argv = {"placeholder"}; + for (NSUInteger i = 0; i < arguments.count; ++i) { + argv.push_back([arguments[i] UTF8String]); + } + + NSString* icuData = [[NSBundle bundleForClass:[self class]] pathForResource:kICUBundlePath + ofType:nil]; + + FlutterProjectArgs flutterArguments = {}; + flutterArguments.struct_size = sizeof(FlutterProjectArgs); + flutterArguments.assets_path = assets.fileSystemRepresentation; + flutterArguments.icu_data_path = icuData.UTF8String; + flutterArguments.command_line_argc = static_cast(argv.size()); + flutterArguments.command_line_argv = &argv[0]; + flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; + + FlutterEngineResult result = FlutterEngineRun( + FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine); + if (result != kSuccess) { + NSLog(@"Failed to start Flutter engine: error %d", result); + return NO; + } + return YES; +} + +- (id)binaryMessenger { + // TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins + // keeping the engine alive. + return self; +} + +#pragma mark - Framework-internal methods + +- (void)updateWindowMetricsWithSize:(CGSize)size pixelRatio:(double)pixelRatio { + const FlutterWindowMetricsEvent event = { + .struct_size = sizeof(event), + .width = static_cast(size.width), + .height = static_cast(size.height), + .pixel_ratio = pixelRatio, + }; + FlutterEngineSendWindowMetricsEvent(_engine, &event); +} + +- (void)sendPointerEvent:(const FlutterPointerEvent&)event { + FlutterEngineSendPointerEvent(_engine, &event, 1); +} + +#pragma mark - Private methods + +- (bool)engineCallbackOnMakeCurrent { + if (!_viewController.view) { + return false; + } + [_viewController.view makeCurrentContext]; + return true; +} + +- (bool)engineCallbackOnClearCurrent { + if (!_viewController.view) { + return false; + } + [NSOpenGLContext clearCurrentContext]; + return true; +} + +- (bool)engineCallbackOnPresent { + if (!_viewController.view) { + return false; + } + [_viewController.view onPresent]; + return true; +} + +- (bool)engineCallbackOnMakeResourceCurrent { + if (!_viewController.view) { + return false; + } + [_viewController makeResourceContextCurrent]; + return true; +} + +- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { + NSData* messageData = [NSData dataWithBytesNoCopy:(void*)message->message + length:message->message_size + freeWhenDone:NO]; + NSString* channel = @(message->channel); + __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle; + + FlutterBinaryReply binaryResponseHandler = ^(NSData* response) { + if (responseHandle) { + FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, + static_cast(response.bytes), + response.length); + responseHandle = NULL; + } else { + NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response " + "on channel '%@'.", + channel); + } + }; + + FlutterBinaryMessageHandler channelHandler = _messageHandlers[channel]; + if (channelHandler) { + channelHandler(messageData, binaryResponseHandler); + } else { + binaryResponseHandler(nil); + } +} + +#pragma mark - FlutterBinaryMessenger + +- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { + FlutterPlatformMessage platformMessage = { + .struct_size = sizeof(FlutterPlatformMessage), + .channel = [channel UTF8String], + .message = static_cast(message.bytes), + .message_size = message.length, + }; + + FlutterEngineResult result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); + if (result != kSuccess) { + NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel, result); + } +} + +- (void)setMessageHandlerOnChannel:(nonnull NSString*)channel + binaryMessageHandler:(nullable FlutterBinaryMessageHandler)handler { + _messageHandlers[channel] = [handler copy]; +} + +#pragma mark - FlutterPluginRegistry + +- (id)registrarForPlugin:(NSString*)pluginName { + return [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self]; +} + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h b/shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h new file mode 100644 index 0000000000000..80112e205c9e0 --- /dev/null +++ b/shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEEngine.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. + */ +- (void)updateWindowMetricsWithSize:(CGSize)size pixelRatio:(double)pixelRatio; + +/** + * Dispatches the given pointer event data to engine. + */ +- (void)sendPointerEvent:(const FlutterPointerEvent&)event; + +@end diff --git a/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm b/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm index eeb1ebd9d193f..158d359028c34 100644 --- a/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm +++ b/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.mm @@ -72,7 +72,7 @@ - (instancetype)initWithViewController:(FLEViewController*)viewController { if (self != nil) { _flutterViewController = viewController; _channel = [FlutterMethodChannel methodChannelWithName:kTextInputChannel - binaryMessenger:viewController.binaryMessenger + binaryMessenger:viewController.engine.binaryMessenger codec:[FlutterJSONMethodCodec sharedInstance]]; __weak FLETextInputPlugin* weakSelf = self; [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { diff --git a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm index e0cdeaa4b37b5..8bc65d67625c1 100644 --- a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm @@ -7,15 +7,13 @@ #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterCodecs.h" +#import "flutter/shell/platform/darwin/macos/framework/Headers/FLEEngine.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h" #import "flutter/shell/platform/darwin/macos/framework/Headers/FLEView.h" +#import "flutter/shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h" #import "flutter/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h" #import "flutter/shell/platform/embedder/embedder.h" -static NSString* const kICUBundlePath = @"icudtl.dat"; - -static const int kDefaultWindowFramebuffer = 0; - namespace { /// Clipboard plain text format. @@ -70,7 +68,7 @@ void Reset() { /** * Private interface declaration for FLEViewController. */ -@interface FLEViewController () +@interface FLEViewController () /** * A list of additional responders to keyboard events. Keybord events are forwarded to all of them. @@ -98,35 +96,11 @@ - (void)configureTrackingArea; */ - (void)addInternalPlugins; -/** - * Shared implementation of the regular and headless public APIs. - */ -- (BOOL)launchEngineInternalWithAssetsPath:(nonnull NSURL*)assets - headless:(BOOL)headless - commandLineArguments:(nullable NSArray*)arguments; - -/** - * Creates a render config with callbacks based on whether the embedder is being run as a headless - * server. - */ -+ (FlutterRendererConfig)createRenderConfigHeadless:(BOOL)headless; - /** * Creates the OpenGL context used as the resource context by the engine. */ - (void)createResourceContext; -/** - * Makes the OpenGL context used by the engine for rendering optimization the - * current context. - */ -- (void)makeResourceContextCurrent; - -/** - * Responds to system messages sent to this controller from the Flutter engine. - */ -- (void)handlePlatformMessage:(const FlutterPlatformMessage*)message; - /** * Calls dispatchMouseEvent:phase: with a phase determined by self.mouseState. * @@ -173,88 +147,14 @@ - (void)setClipboardData:(NSDictionary*)data; @end -#pragma mark - Static methods provided to engine configuration - -/** - * Makes the owned FlutterView the current context. - */ -static bool OnMakeCurrent(FLEViewController* controller) { - [controller.view makeCurrentContext]; - return true; -} - -/** - * Clears the current context. - */ -static bool OnClearCurrent(FLEViewController* controller) { - [NSOpenGLContext clearCurrentContext]; - return true; -} - -/** - * Flushes the GL context as part of the Flutter rendering pipeline. - */ -static bool OnPresent(FLEViewController* controller) { - [controller.view onPresent]; - return true; -} - -/** - * Returns the framebuffer object whose color attachment the engine should render into. - */ -static uint32_t OnFBO(FLEViewController* controller) { - return kDefaultWindowFramebuffer; -} - -/** - * Handles the given platform message by dispatching to the controller. - */ -static void OnPlatformMessage(const FlutterPlatformMessage* message, - FLEViewController* controller) { - [controller handlePlatformMessage:message]; -} - -/** - * Makes the resource context the current context. - */ -static bool OnMakeResourceCurrent(FLEViewController* controller) { - [controller makeResourceContextCurrent]; - return true; -} - -#pragma mark Static methods provided for headless engine configuration - -static bool HeadlessOnMakeCurrent(FLEViewController* controller) { - return false; -} - -static bool HeadlessOnClearCurrent(FLEViewController* controller) { - return false; -} - -static bool HeadlessOnPresent(FLEViewController* controller) { - return false; -} - -static uint32_t HeadlessOnFBO(FLEViewController* controller) { - return kDefaultWindowFramebuffer; -} - -static bool HeadlessOnMakeResourceCurrent(FLEViewController* controller) { - return false; -} - #pragma mark - FLEViewController implementation. @implementation FLEViewController { - FlutterEngine _engine; + FLEEngine* _engine; // The additional context provided to the Flutter engine for resource loading. NSOpenGLContext* _resourceContext; - // A mapping of channel names to the registered handlers for those channels. - NSMutableDictionary* _messageHandlers; - // The plugin used to handle text input. This is not an FlutterPlugin, so must be owned // separately. FLETextInputPlugin* _textInputPlugin; @@ -276,7 +176,7 @@ @implementation FLEViewController { * Performs initialization that's common between the different init paths. */ static void CommonInit(FLEViewController* controller) { - controller->_messageHandlers = [[NSMutableDictionary alloc] init]; + controller->_engine = [[FLEEngine alloc] initWithViewController:controller]; controller->_additionalKeyResponders = [[NSMutableOrderedSet alloc] init]; controller->_mouseTrackingMode = FlutterMouseTrackingModeInKeyWindow; } @@ -297,12 +197,6 @@ - (instancetype)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBun return self; } -- (void)dealloc { - if (FlutterEngineShutdown(_engine) == kSuccess) { - _engine = NULL; - } -} - - (void)setView:(NSView*)view { if (_trackingArea) { [self.view removeTrackingArea:_trackingArea]; @@ -327,16 +221,21 @@ - (void)setMouseTrackingMode:(FlutterMouseTrackingMode)mode { - (BOOL)launchEngineWithAssetsPath:(NSURL*)assets commandLineArguments:(NSArray*)arguments { - return [self launchEngineInternalWithAssetsPath:assets - headless:NO - commandLineArguments:arguments]; -} + // 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]; -- (BOOL)launchHeadlessEngineWithAssetsPath:(NSURL*)assets - commandLineArguments:(NSArray*)arguments { - return [self launchEngineInternalWithAssetsPath:assets - headless:YES - commandLineArguments:arguments]; + if (![_engine launchEngineWithAssetsPath:assets commandLineArguments:arguments]) { + 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 @@ -349,6 +248,10 @@ - (void)removeKeyResponder:(NSResponder*)responder { [self.additionalKeyResponders removeObject:responder]; } +- (void)makeResourceContextCurrent { + [_resourceContext makeCurrentContext]; +} + #pragma mark - Private methods - (void)configureTrackingArea { @@ -384,15 +287,15 @@ - (void)addInternalPlugins { _textInputPlugin = [[FLETextInputPlugin alloc] initWithViewController:self]; _keyEventChannel = [FlutterBasicMessageChannel messageChannelWithName:@"flutter/keyevent" - binaryMessenger:self + binaryMessenger:_engine.binaryMessenger codec:[FlutterJSONMessageCodec sharedInstance]]; _settingsChannel = [FlutterBasicMessageChannel messageChannelWithName:@"flutter/settings" - binaryMessenger:self + binaryMessenger:_engine.binaryMessenger codec:[FlutterJSONMessageCodec sharedInstance]]; _platformChannel = [FlutterMethodChannel methodChannelWithName:@"flutter/platform" - binaryMessenger:self + binaryMessenger:_engine.binaryMessenger codec:[FlutterJSONMethodCodec sharedInstance]]; __weak FLEViewController* weakSelf = self; [_platformChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { @@ -400,119 +303,12 @@ - (void)addInternalPlugins { }]; } -- (BOOL)launchEngineInternalWithAssetsPath:(NSURL*)assets - headless:(BOOL)headless - commandLineArguments:(NSArray*)arguments { - if (_engine != NULL) { - return NO; - } - - // 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]; - - const FlutterRendererConfig config = [FLEViewController createRenderConfigHeadless:headless]; - - // Register internal plugins before starting the engine. - [self addInternalPlugins]; - - // FlutterProjectArgs is expecting a full argv, so when processing it for flags the first - // item is treated as the executable and ignored. Add a dummy value so that all provided arguments - // are used. - const unsigned long argc = arguments.count + 1; - const char** argv = (const char**)malloc(argc * sizeof(const char*)); - argv[0] = "placeholder"; - for (NSUInteger i = 0; i < arguments.count; ++i) { - argv[i + 1] = [arguments[i] UTF8String]; - } - - NSString* icuData = [[NSBundle bundleForClass:[self class]] pathForResource:kICUBundlePath - ofType:nil]; - - FlutterProjectArgs flutterArguments = {}; - flutterArguments.struct_size = sizeof(FlutterProjectArgs); - flutterArguments.assets_path = assets.fileSystemRepresentation; - flutterArguments.icu_data_path = icuData.UTF8String; - flutterArguments.command_line_argc = (int)(argc); - flutterArguments.command_line_argv = argv; - flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; - - FlutterEngineResult result = FlutterEngineRun(FLUTTER_ENGINE_VERSION, &config, &flutterArguments, - (__bridge void*)(self), &_engine); - free(argv); - if (result != kSuccess) { - NSLog(@"Failed to start Flutter engine: error %d", result); - return NO; - } - // Send the initial user settings such as brightness and text scale factor - // to the engine. - [self sendInitialSettings]; - return YES; -} - -+ (FlutterRendererConfig)createRenderConfigHeadless:(BOOL)headless { - if (headless) { - const FlutterRendererConfig config = { - .type = kOpenGL, - .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), - .open_gl.make_current = (BoolCallback)HeadlessOnMakeCurrent, - .open_gl.clear_current = (BoolCallback)HeadlessOnClearCurrent, - .open_gl.present = (BoolCallback)HeadlessOnPresent, - .open_gl.fbo_callback = (UIntCallback)HeadlessOnFBO, - .open_gl.make_resource_current = (BoolCallback)HeadlessOnMakeResourceCurrent}; - return config; - } else { - const FlutterRendererConfig config = { - .type = kOpenGL, - .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), - .open_gl.make_current = (BoolCallback)OnMakeCurrent, - .open_gl.clear_current = (BoolCallback)OnClearCurrent, - .open_gl.present = (BoolCallback)OnPresent, - .open_gl.fbo_callback = (UIntCallback)OnFBO, - .open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent}; - return config; - } -} - - (void)createResourceContext { NSOpenGLContext* viewContext = ((NSOpenGLView*)self.view).openGLContext; _resourceContext = [[NSOpenGLContext alloc] initWithFormat:viewContext.pixelFormat shareContext:viewContext]; } -- (void)makeResourceContextCurrent { - [_resourceContext makeCurrentContext]; -} - -- (void)handlePlatformMessage:(const FlutterPlatformMessage*)message { - NSData* messageData = [NSData dataWithBytesNoCopy:(void*)message->message - length:message->message_size - freeWhenDone:NO]; - NSString* channel = @(message->channel); - __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle; - - FlutterBinaryReply binaryResponseHandler = ^(NSData* response) { - if (responseHandle) { - FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, - static_cast(response.bytes), - response.length); - responseHandle = NULL; - } else { - NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response " - "on channel '%@'.", - channel); - } - }; - - FlutterBinaryMessageHandler channelHandler = _messageHandlers[channel]; - if (channelHandler) { - channelHandler(messageData, binaryResponseHandler); - } else { - binaryResponseHandler(nil); - } -} - - (void)dispatchMouseEvent:(nonnull NSEvent*)event { FlutterPointerPhase phase = _mouseState.buttons == 0 ? (_mouseState.flutter_state_is_down ? kUp : kHover) @@ -572,7 +368,7 @@ - (void)dispatchMouseEvent:(NSEvent*)event phase:(FlutterPointerPhase)phase { flutterEvent.scroll_delta_x = -event.scrollingDeltaX * pixelsPerLine * scaleFactor; flutterEvent.scroll_delta_y = -event.scrollingDeltaY * pixelsPerLine * scaleFactor; } - FlutterEngineSendPointerEvent(_engine, &flutterEvent, 1); + [_engine sendPointerEvent:flutterEvent]; // Update tracking of state as reported to Flutter. if (phase == kDown) { @@ -661,61 +457,15 @@ - (void)setClipboardData:(NSDictionary*)data { * Responds to view reshape by notifying the engine of the change in dimensions. */ - (void)viewDidReshape:(NSOpenGLView*)view { - CGRect scaledBounds = [view convertRectToBacking:view.bounds]; - const FlutterWindowMetricsEvent event = { - .struct_size = sizeof(event), - .width = static_cast(scaledBounds.size.width), - .height = static_cast(scaledBounds.size.height), - .pixel_ratio = scaledBounds.size.width / view.bounds.size.width, - }; - FlutterEngineSendWindowMetricsEvent(_engine, &event); -} - -#pragma mark - FlutterBinaryMessengerContainer -- (NSObject*)binaryMessenger { - return self; -} - -#pragma mark - FlutterBinaryMessenger - -- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { - FlutterPlatformMessage platformMessage = { - .struct_size = sizeof(FlutterPlatformMessage), - .channel = [channel UTF8String], - .message = static_cast(message.bytes), - .message_size = message.length, - }; - - FlutterEngineResult result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); - if (result != kSuccess) { - NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel, result); - } -} - -- (void)setMessageHandlerOnChannel:(nonnull NSString*)channel - binaryMessageHandler:(nullable FlutterBinaryMessageHandler)handler { - _messageHandlers[channel] = [handler copy]; -} - -#pragma mark - FlutterPluginRegistrar - -- (id)messenger { - return self; -} - -- (void)addMethodCallDelegate:(nonnull id)delegate - channel:(nonnull FlutterMethodChannel*)channel { - [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { - [delegate handleMethodCall:call result:result]; - }]; + 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]; } #pragma mark - FlutterPluginRegistry - (id)registrarForPlugin:(NSString*)pluginName { - // Currently, the view controller acts as the registrar for all plugins, so the - // name is ignored. - return self; + return [_engine registrarForPlugin:pluginName]; } #pragma mark - NSResponder diff --git a/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h b/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h index 8fd3fb26519c8..540010db478e7 100644 --- a/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h +++ b/shell/platform/darwin/macos/framework/Source/FLEViewController_Internal.h @@ -17,4 +17,10 @@ */ - (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 From af0b6d0c906c6961a0a7366c19c1df9c6c3023df Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 2 Jul 2019 16:40:18 -0700 Subject: [PATCH 2/4] Add new header to umbrella --- shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h | 1 + 1 file changed, 1 insertion(+) diff --git a/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h b/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h index 8237ecfa676fc..317370ea203a1 100644 --- a/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h +++ b/shell/platform/darwin/macos/framework/Headers/FlutterMacOS.h @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#import "FLEEngine.h" #import "FLEOpenGLContextHandling.h" #import "FLEReshapeListener.h" #import "FLEView.h" From 8ca70132279a5130c9f0049a71849d11d226fb0d Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 2 Jul 2019 16:41:28 -0700 Subject: [PATCH 3/4] Update license file --- ci/licenses_golden/licenses_flutter | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 1418af878d6d9..f355ef5d891d1 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -739,6 +739,7 @@ FILE: ../../../flutter/shell/platform/darwin/ios/ios_surface_software.mm FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.h FILE: ../../../flutter/shell/platform/darwin/ios/platform_view_ios.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/FlutterMacOS.podspec +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEEngine.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEOpenGLContextHandling.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEReshapeListener.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FLEView.h @@ -747,6 +748,8 @@ FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterMacO FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginMacOS.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Headers/FlutterPluginRegistrarMacOS.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Info.plist +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLEEngine.mm +FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLEEngine_Internal.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputModel.h FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputModel.mm FILE: ../../../flutter/shell/platform/darwin/macos/framework/Source/FLETextInputPlugin.h From 41f2f41717c66130118a5297c228294466a65e7b Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Wed, 3 Jul 2019 12:05:02 -0700 Subject: [PATCH 4/4] Remove accidental duplicate _engine ivar --- .../platform/darwin/macos/framework/Source/FLEViewController.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm index 8bc65d67625c1..a7872ac9ae55c 100644 --- a/shell/platform/darwin/macos/framework/Source/FLEViewController.mm +++ b/shell/platform/darwin/macos/framework/Source/FLEViewController.mm @@ -150,8 +150,6 @@ - (void)setClipboardData:(NSDictionary*)data; #pragma mark - FLEViewController implementation. @implementation FLEViewController { - FLEEngine* _engine; - // The additional context provided to the Flutter engine for resource loading. NSOpenGLContext* _resourceContext;