• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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