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 5#import "flutter/shell/platform/darwin/macos/framework/Headers/FlutterEngine.h" 6#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h" 7 8#include <vector> 9 10#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterDartProject_Internal.h" 11#import "flutter/shell/platform/darwin/macos/framework/Source/FlutterViewController_Internal.h" 12#import "flutter/shell/platform/embedder/embedder.h" 13 14/** 15 * Private interface declaration for FlutterEngine. 16 */ 17@interface FlutterEngine () <FlutterBinaryMessenger> 18 19/** 20 * Called by the engine to make the context the engine should draw into current. 21 */ 22- (bool)engineCallbackOnMakeCurrent; 23 24/** 25 * Called by the engine to clear the context the engine should draw into. 26 */ 27- (bool)engineCallbackOnClearCurrent; 28 29/** 30 * Called by the engine when the context's buffers should be swapped. 31 */ 32- (bool)engineCallbackOnPresent; 33 34/** 35 * Makes the resource context the current context. 36 */ 37- (bool)engineCallbackOnMakeResourceCurrent; 38 39/** 40 * Handles a platform message from the engine. 41 */ 42- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message; 43 44/** 45 * Shuts the Flutter engine if it is running. 46 */ 47- (void)shutDownEngine; 48 49@end 50 51#pragma mark - 52 53/** 54 * `FlutterPluginRegistrar` implementation handling a single plugin. 55 */ 56@interface FlutterEngineRegistrar : NSObject <FlutterPluginRegistrar> 57- (instancetype)initWithPlugin:(nonnull NSString*)pluginKey 58 flutterEngine:(nonnull FlutterEngine*)flutterEngine; 59@end 60 61@implementation FlutterEngineRegistrar { 62 NSString* _pluginKey; 63 FlutterEngine* _flutterEngine; 64} 65 66- (instancetype)initWithPlugin:(NSString*)pluginKey flutterEngine:(FlutterEngine*)flutterEngine { 67 self = [super init]; 68 if (self) { 69 _pluginKey = [pluginKey copy]; 70 _flutterEngine = flutterEngine; 71 } 72 return self; 73} 74 75#pragma mark - FlutterPluginRegistrar 76 77- (id<FlutterBinaryMessenger>)messenger { 78 return _flutterEngine.binaryMessenger; 79} 80 81- (NSView*)view { 82 return _flutterEngine.viewController.view; 83} 84 85- (void)addMethodCallDelegate:(nonnull id<FlutterPlugin>)delegate 86 channel:(nonnull FlutterMethodChannel*)channel { 87 [channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 88 [delegate handleMethodCall:call result:result]; 89 }]; 90} 91 92@end 93 94// Callbacks provided to the engine. See the called methods for documentation. 95#pragma mark - Static methods provided to engine configuration 96 97static bool OnMakeCurrent(FlutterEngine* engine) { 98 return [engine engineCallbackOnMakeCurrent]; 99} 100 101static bool OnClearCurrent(FlutterEngine* engine) { 102 return [engine engineCallbackOnClearCurrent]; 103} 104 105static bool OnPresent(FlutterEngine* engine) { 106 return [engine engineCallbackOnPresent]; 107} 108 109static uint32_t OnFBO(FlutterEngine* engine) { 110 // There is currently no case where a different FBO is used, so no need to forward. 111 return 0; 112} 113 114static bool OnMakeResourceCurrent(FlutterEngine* engine) { 115 return [engine engineCallbackOnMakeResourceCurrent]; 116} 117 118static void OnPlatformMessage(const FlutterPlatformMessage* message, FlutterEngine* engine) { 119 [engine engineCallbackOnPlatformMessage:message]; 120} 121 122#pragma mark - 123 124@implementation FlutterEngine { 125 // The embedding-API-level engine object. 126 FLUTTER_API_SYMBOL(FlutterEngine) _engine; 127 128 // The project being run by this engine. 129 FlutterDartProject* _project; 130 131 // The context provided to the Flutter engine for resource loading. 132 NSOpenGLContext* _resourceContext; 133 134 // A mapping of channel names to the registered handlers for those channels. 135 NSMutableDictionary<NSString*, FlutterBinaryMessageHandler>* _messageHandlers; 136 137 // Whether the engine can continue running after the view controller is removed. 138 BOOL _allowHeadlessExecution; 139} 140 141- (instancetype)initWithName:(NSString*)labelPrefix project:(FlutterDartProject*)project { 142 return [self initWithName:labelPrefix project:project allowHeadlessExecution:YES]; 143} 144 145- (instancetype)initWithName:(NSString*)labelPrefix 146 project:(FlutterDartProject*)project 147 allowHeadlessExecution:(BOOL)allowHeadlessExecution { 148 self = [super init]; 149 NSAssert(self, @"Super init cannot be nil"); 150 151 _project = project ?: [[FlutterDartProject alloc] init]; 152 _messageHandlers = [[NSMutableDictionary alloc] init]; 153 _allowHeadlessExecution = allowHeadlessExecution; 154 155 return self; 156} 157 158- (void)dealloc { 159 [self shutDownEngine]; 160} 161 162- (BOOL)runWithEntrypoint:(NSString*)entrypoint { 163 if (self.running) { 164 return NO; 165 } 166 167 if (!_allowHeadlessExecution && !_viewController) { 168 NSLog(@"Attempted to run an engine with no view controller without headless mode enabled."); 169 return NO; 170 } 171 172 const FlutterRendererConfig rendererConfig = { 173 .type = kOpenGL, 174 .open_gl.struct_size = sizeof(FlutterOpenGLRendererConfig), 175 .open_gl.make_current = (BoolCallback)OnMakeCurrent, 176 .open_gl.clear_current = (BoolCallback)OnClearCurrent, 177 .open_gl.present = (BoolCallback)OnPresent, 178 .open_gl.fbo_callback = (UIntCallback)OnFBO, 179 .open_gl.make_resource_current = (BoolCallback)OnMakeResourceCurrent, 180 }; 181 182 // TODO(stuartmorgan): Move internal channel registration from FlutterViewController to here. 183 184 FlutterProjectArgs flutterArguments = {}; 185 flutterArguments.struct_size = sizeof(FlutterProjectArgs); 186 flutterArguments.assets_path = _project.assetsPath.UTF8String; 187 flutterArguments.icu_data_path = _project.ICUDataPath.UTF8String; 188 std::vector<const char*> arguments = _project.argv; 189 flutterArguments.command_line_argc = static_cast<int>(arguments.size()); 190 flutterArguments.command_line_argv = &arguments[0]; 191 flutterArguments.platform_message_callback = (FlutterPlatformMessageCallback)OnPlatformMessage; 192 flutterArguments.custom_dart_entrypoint = entrypoint.UTF8String; 193 194 FlutterEngineResult result = FlutterEngineRun( 195 FLUTTER_ENGINE_VERSION, &rendererConfig, &flutterArguments, (__bridge void*)(self), &_engine); 196 if (result != kSuccess) { 197 NSLog(@"Failed to start Flutter engine: error %d", result); 198 return NO; 199 } 200 [self updateWindowMetrics]; 201 return YES; 202} 203 204- (void)setViewController:(FlutterViewController*)controller { 205 _viewController = controller; 206 if (!controller && !_allowHeadlessExecution) { 207 [self shutDownEngine]; 208 _resourceContext = nil; 209 } 210 [self updateWindowMetrics]; 211} 212 213- (id<FlutterBinaryMessenger>)binaryMessenger { 214 // TODO(stuartmorgan): Switch to FlutterBinaryMessengerRelay to avoid plugins 215 // keeping the engine alive. 216 return self; 217} 218 219#pragma mark - Framework-internal methods 220 221- (BOOL)running { 222 return _engine != nullptr; 223} 224 225- (NSOpenGLContext*)resourceContext { 226 if (!_resourceContext) { 227 NSOpenGLPixelFormatAttribute attributes[] = { 228 NSOpenGLPFAColorSize, 24, NSOpenGLPFAAlphaSize, 8, NSOpenGLPFADoubleBuffer, 0, 229 }; 230 NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; 231 _resourceContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; 232 } 233 return _resourceContext; 234} 235 236- (void)updateWindowMetrics { 237 if (!_engine) { 238 return; 239 } 240 NSView* view = _viewController.view; 241 CGSize scaledSize = [view convertRectToBacking:view.bounds].size; 242 double pixelRatio = view.bounds.size.width == 0 ? 1 : scaledSize.width / view.bounds.size.width; 243 244 const FlutterWindowMetricsEvent event = { 245 .struct_size = sizeof(event), 246 .width = static_cast<size_t>(scaledSize.width), 247 .height = static_cast<size_t>(scaledSize.height), 248 .pixel_ratio = pixelRatio, 249 }; 250 FlutterEngineSendWindowMetricsEvent(_engine, &event); 251} 252 253- (void)sendPointerEvent:(const FlutterPointerEvent&)event { 254 FlutterEngineSendPointerEvent(_engine, &event, 1); 255} 256 257#pragma mark - Private methods 258 259- (bool)engineCallbackOnMakeCurrent { 260 if (!_viewController.flutterView) { 261 return false; 262 } 263 [_viewController.flutterView makeCurrentContext]; 264 return true; 265} 266 267- (bool)engineCallbackOnClearCurrent { 268 if (!_viewController.flutterView) { 269 return false; 270 } 271 [NSOpenGLContext clearCurrentContext]; 272 return true; 273} 274 275- (bool)engineCallbackOnPresent { 276 if (!_viewController.flutterView) { 277 return false; 278 } 279 [_viewController.flutterView onPresent]; 280 return true; 281} 282 283- (bool)engineCallbackOnMakeResourceCurrent { 284 if (!_viewController.flutterView) { 285 return false; 286 } 287 [self.resourceContext makeCurrentContext]; 288 return true; 289} 290 291- (void)engineCallbackOnPlatformMessage:(const FlutterPlatformMessage*)message { 292 NSData* messageData = [NSData dataWithBytesNoCopy:(void*)message->message 293 length:message->message_size 294 freeWhenDone:NO]; 295 NSString* channel = @(message->channel); 296 __block const FlutterPlatformMessageResponseHandle* responseHandle = message->response_handle; 297 298 FlutterBinaryReply binaryResponseHandler = ^(NSData* response) { 299 if (responseHandle) { 300 FlutterEngineSendPlatformMessageResponse(self->_engine, responseHandle, 301 static_cast<const uint8_t*>(response.bytes), 302 response.length); 303 responseHandle = NULL; 304 } else { 305 NSLog(@"Error: Message responses can be sent only once. Ignoring duplicate response " 306 "on channel '%@'.", 307 channel); 308 } 309 }; 310 311 FlutterBinaryMessageHandler channelHandler = _messageHandlers[channel]; 312 if (channelHandler) { 313 channelHandler(messageData, binaryResponseHandler); 314 } else { 315 binaryResponseHandler(nil); 316 } 317} 318 319/** 320 * Note: Called from dealloc. Should not use accessors or other methods. 321 */ 322- (void)shutDownEngine { 323 if (_engine) { 324 FlutterEngineResult result = FlutterEngineShutdown(_engine); 325 if (result != kSuccess) { 326 NSLog(@"Failed to shut down Flutter engine: error %d", result); 327 } 328 } 329 _engine = nullptr; 330} 331 332#pragma mark - FlutterBinaryMessenger 333 334- (void)sendOnChannel:(nonnull NSString*)channel message:(nullable NSData*)message { 335 [self sendOnChannel:channel message:message binaryReply:nil]; 336} 337 338- (void)sendOnChannel:(NSString*)channel 339 message:(NSData* _Nullable)message 340 binaryReply:(FlutterBinaryReply _Nullable)callback { 341 FlutterPlatformMessageResponseHandle* response_handle = nullptr; 342 if (callback) { 343 struct Captures { 344 FlutterBinaryReply reply; 345 }; 346 auto captures = std::make_unique<Captures>(); 347 captures->reply = callback; 348 auto message_reply = [](const uint8_t* data, size_t data_size, void* user_data) { 349 auto captures = reinterpret_cast<Captures*>(user_data); 350 NSData* reply_data = nil; 351 if (data != nullptr && data_size > 0) { 352 reply_data = [NSData dataWithBytes:static_cast<const void*>(data) length:data_size]; 353 } 354 captures->reply(reply_data); 355 delete captures; 356 }; 357 358 FlutterEngineResult create_result = FlutterPlatformMessageCreateResponseHandle( 359 _engine, message_reply, captures.get(), &response_handle); 360 if (create_result != kSuccess) { 361 NSLog(@"Failed to create a FlutterPlatformMessageResponseHandle (%d)", create_result); 362 return; 363 } 364 captures.release(); 365 } 366 367 FlutterPlatformMessage platformMessage = { 368 .struct_size = sizeof(FlutterPlatformMessage), 369 .channel = [channel UTF8String], 370 .message = static_cast<const uint8_t*>(message.bytes), 371 .message_size = message.length, 372 .response_handle = response_handle, 373 }; 374 375 FlutterEngineResult message_result = FlutterEngineSendPlatformMessage(_engine, &platformMessage); 376 if (message_result != kSuccess) { 377 NSLog(@"Failed to send message to Flutter engine on channel '%@' (%d).", channel, 378 message_result); 379 } 380 381 if (response_handle != nullptr) { 382 FlutterEngineResult release_result = 383 FlutterPlatformMessageReleaseResponseHandle(_engine, response_handle); 384 if (release_result != kSuccess) { 385 NSLog(@"Failed to release the response handle (%d).", release_result); 386 }; 387 } 388} 389 390- (void)setMessageHandlerOnChannel:(nonnull NSString*)channel 391 binaryMessageHandler:(nullable FlutterBinaryMessageHandler)handler { 392 _messageHandlers[channel] = [handler copy]; 393} 394 395#pragma mark - FlutterPluginRegistry 396 397- (id<FlutterPluginRegistrar>)registrarForPlugin:(NSString*)pluginName { 398 return [[FlutterEngineRegistrar alloc] initWithPlugin:pluginName flutterEngine:self]; 399} 400 401@end 402