sky_surface.mm 13.4 KB
Newer Older
C
Chinmay Garde 已提交
1 2 3 4 5 6 7 8 9 10
// Copyright 2015 The Chromium 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 "sky_surface.h"

#import <QuartzCore/QuartzCore.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/EAGLDrawable.h>

11
#include "base/command_line.h"
12
#include "base/logging.h"
13
#include "base/time/time.h"
C
Chinmay Garde 已提交
14
#include "base/trace_event/trace_event.h"
15 16
#include "mojo/public/cpp/bindings/interface_request.h"
#include "sky/services/engine/input_event.mojom.h"
17
#include "sky/services/pointer/pointer.mojom.h"
18
#include "sky/shell/platform/ios/sky_dynamic_service_loader.h"
19
#include "sky/shell/platform/mac/platform_mac.h"
20
#include "sky/shell/platform/mac/platform_service_provider.h"
21
#include "sky/shell/platform/mac/platform_view_mac.h"
22
#include "sky/shell/shell.h"
23
#include "sky/shell/shell_view.h"
24
#include "sky/shell/switches.h"
25
#include "sky/shell/ui_delegate.h"
26
#include <strings.h>
C
Chinmay Garde 已提交
27

28 29 30 31 32 33
enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

34 35
using PointerTypeMapperPhase = std::pair<pointer::PointerType, MapperPhase>;
static inline PointerTypeMapperPhase PointerTypePhaseFromUITouchPhase(
36
    UITouchPhase phase) {
C
Chinmay Garde 已提交
37 38
  switch (phase) {
    case UITouchPhaseBegan:
39 40
      return PointerTypeMapperPhase(pointer::PointerType::DOWN,
                                    MapperPhase::Added);
C
Chinmay Garde 已提交
41 42 43 44
    case UITouchPhaseMoved:
    case UITouchPhaseStationary:
      // There is no EVENT_TYPE_POINTER_STATIONARY. So we just pass a move type
      // with the same coordinates
45 46
      return PointerTypeMapperPhase(pointer::PointerType::MOVE,
                                    MapperPhase::Accessed);
C
Chinmay Garde 已提交
47
    case UITouchPhaseEnded:
48 49
      return PointerTypeMapperPhase(pointer::PointerType::UP,
                                    MapperPhase::Removed);
50
    case UITouchPhaseCancelled:
51 52
      return PointerTypeMapperPhase(pointer::PointerType::CANCEL,
                                    MapperPhase::Removed);
C
Chinmay Garde 已提交
53 54
  }

55 56
  return PointerTypeMapperPhase(pointer::PointerType::CANCEL,
                                MapperPhase::Accessed);
C
Chinmay Garde 已提交
57 58
}

59 60
static inline int64 InputEventTimestampFromNSTimeInterval(
    NSTimeInterval interval) {
A
Adam Barth 已提交
61
  return base::TimeDelta::FromSecondsD(interval).InMicroseconds();
62 63
}

64 65 66 67 68 69 70 71 72 73 74 75 76 77
// UITouch pointers cannot be used as touch ids (even though they remain
// constant throughout the multitouch sequence) because internal components
// assume that ids are < 16. This class maps touch pointers to ids
class TouchMapper {
 public:
  TouchMapper() : free_spots_(~0) {}

  int registerTouch(uintptr_t touch) {
    int freeSpot = ffsll(free_spots_);
    touch_map_[touch] = freeSpot;
    free_spots_ &= ~(1 << (freeSpot - 1));
    return freeSpot;
  }

78
  int unregisterTouch(uintptr_t touch) {
79 80 81
    auto index = touch_map_[touch];
    free_spots_ |= 1 << (index - 1);
    touch_map_.erase(touch);
82
    return index;
83 84 85 86 87 88 89 90 91 92
  }

  int identifierOf(uintptr_t touch) { return touch_map_[touch]; }

 private:
  using BitSet = long long int;
  BitSet free_spots_;
  std::map<uintptr_t, int> touch_map_;
};

93 94 95 96 97 98 99 100 101
static void DynamicServiceResolve(void* baton,
                                  const mojo::String& service_name,
                                  mojo::ScopedMessagePipeHandle handle) {
  @autoreleasepool {
    auto loader = reinterpret_cast<SkyDynamicServiceLoader*>(baton);
    [loader resolveService:@(service_name.data()) handle:handle.Pass()];
  }
}

102 103 104 105
@interface SkySurface ()<UIInputViewAudioFeedback>

@end

C
Chinmay Garde 已提交
106 107
@implementation SkySurface {
  BOOL _platformViewInitialized;
108
  CGPoint _lastScrollTranslation;
109
  sky::ViewportMetricsPtr _viewportMetrics;
C
Chinmay Garde 已提交
110

111
  sky::SkyEnginePtr _engine;
112
  SkyDynamicServiceLoader* _dynamic_service_loader;
113
  std::unique_ptr<sky::shell::ShellView> _shell_view;
114
  TouchMapper _touch_mapper;
115 116
}

117
static std::string TracesBasePath() {
118 119
  NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                       NSUserDomainMask, YES);
120
  return [paths.firstObject UTF8String];
121 122
}

123
- (instancetype)initWithShellView:(sky::shell::ShellView*)shellView {
C
Chinmay Garde 已提交
124
  TRACE_EVENT0("flutter", "initWithShellView");
125
  self = [super init];
126
  if (self) {
127 128
    _viewportMetrics = sky::ViewportMetrics::New();

129 130
    base::FilePath tracesPath =
        base::FilePath::FromUTF8Unsafe(TracesBasePath());
131 132
    sky::shell::Shell::Shared().tracing_controller().set_traces_base_path(
        tracesPath);
133

134
    _shell_view.reset(shellView);
135
    self.multipleTouchEnabled = YES;
136 137 138 139 140 141 142 143 144 145 146 147

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(applicationBecameActive:)
               name:UIApplicationDidBecomeActiveNotification
             object:nil];

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(applicationWillResignActive:)
               name:UIApplicationWillResignActiveNotification
             object:nil];
148

149 150 151 152 153
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(keyboardWasShown:)
               name:UIKeyboardDidShowNotification
             object:nil];
154

155 156 157 158 159
    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(keyboardWillBeHidden:)
               name:UIKeyboardWillHideNotification
             object:nil];
160 161 162 163 164 165

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(onLocaleUpdated:)
               name:NSCurrentLocaleDidChangeNotification
             object:nil];
166
  }
167
  return self;
C
Chinmay Garde 已提交
168 169 170 171 172 173
}

- (gfx::AcceleratedWidget)acceleratedWidget {
  return (gfx::AcceleratedWidget)self.layer;
}

174 175 176 177 178 179 180
- (void)keyboardWasShown:(NSNotification*)notification {
  NSDictionary* info = [notification userInfo];
  CGFloat bottom = CGRectGetHeight(
      [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]);
  CGFloat scale = [UIScreen mainScreen].scale;
  _viewportMetrics->physical_padding_bottom = bottom * scale;
  _engine->OnViewportMetricsChanged(_viewportMetrics.Clone());
181 182
}

183 184 185
- (void)keyboardWillBeHidden:(NSNotification*)notification {
  _viewportMetrics->physical_padding_bottom = 0.0;
  _engine->OnViewportMetricsChanged(_viewportMetrics.Clone());
186 187
}

188 189 190 191 192 193 194
- (void)onLocaleUpdated:(NSNotification*)notification {
    NSLocale *currentLocale = [NSLocale currentLocale];
    NSString *languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
    NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
    _engine->OnLocaleChanged(languageCode.UTF8String, countryCode.UTF8String);
}

C
Chinmay Garde 已提交
195
- (void)layoutSubviews {
C
Chinmay Garde 已提交
196
  TRACE_EVENT0("flutter", "layoutSubviews");
C
Chinmay Garde 已提交
197 198 199 200 201 202 203 204 205
  [super layoutSubviews];

  [self configureLayerDefaults];

  [self setupPlatformViewIfNecessary];

  CGSize size = self.bounds.size;
  CGFloat scale = [UIScreen mainScreen].scale;

206 207 208 209
  _viewportMetrics->device_pixel_ratio = scale;
  _viewportMetrics->physical_width = size.width * scale;
  _viewportMetrics->physical_height = size.height * scale;
  _viewportMetrics->physical_padding_top =
210
      [UIApplication sharedApplication].statusBarFrame.size.height * scale;
211

212
  _engine->OnViewportMetricsChanged(_viewportMetrics.Clone());
C
Chinmay Garde 已提交
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
}

- (void)configureLayerDefaults {
  CAEAGLLayer* layer = reinterpret_cast<CAEAGLLayer*>(self.layer);
  layer.allowsGroupOpacity = YES;
  layer.opaque = YES;
  CGFloat screenScale = [UIScreen mainScreen].scale;
  layer.contentsScale = screenScale;
  // Note: shouldRasterize is still NO. This is just a defensive measure
  layer.rasterizationScale = screenScale;
}

- (void)setupPlatformViewIfNecessary {
  if (_platformViewInitialized) {
    return;
  }

  _platformViewInitialized = YES;

232
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
233 234
}

235 236
- (sky::shell::PlatformViewMac*)platformView {
  auto view = static_cast<sky::shell::PlatformViewMac*>(_shell_view->view());
C
Chinmay Garde 已提交
237 238 239 240
  DCHECK(view);
  return view;
}

241
- (const char*)flxBundlePath {
242 243 244 245
  // In case this runner is part of the precompilation SDK, the FLX bundle is
  // present in the application bundle instead of the runner bundle. Attempt
  // to resolve the path there first.
  NSBundle* applicationBundle = [NSBundle
246
      bundleWithIdentifier:@"io.flutter.application.FlutterApplication"];
247 248 249 250 251 252 253 254
  NSString* path = [applicationBundle pathForResource:@"app" ofType:@"flx"];
  if (path.length != 0) {
    return path.UTF8String;
  }
  return
      [[NSBundle mainBundle] pathForResource:@"app" ofType:@"flx"].UTF8String;
}

255
- (void)connectToEngineAndLoad {
C
Chinmay Garde 已提交
256
  TRACE_EVENT0("flutter", "connectToEngineAndLoad");
257
  self.platformView->ConnectToEngine(mojo::GetProxy(&_engine));
258

259 260
  _dynamic_service_loader = [[SkyDynamicServiceLoader alloc] init];
  void* baton = _dynamic_service_loader;
261
  mojo::ServiceProviderPtr service_provider;
262 263 264
  new sky::shell::PlatformServiceProvider(
      mojo::GetProxy(&service_provider),
      base::Bind(&DynamicServiceResolve, base::Unretained(baton)));
265 266
  sky::ServicesDataPtr services = sky::ServicesData::New();
  services->services_provided_by_embedder = service_provider.Pass();
267
  _engine->SetServices(services.Pass());
268

269 270 271
  // Initialize to current locale
  [self onLocaleUpdated:nil];

272 273 274 275 276 277
#if TARGET_IPHONE_SIMULATOR
  [self runFromDartSource];
#else
  [self runFromPrecompiledSource];
#endif
}
278

279
#if TARGET_IPHONE_SIMULATOR
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

- (void)runFromDartSource {
  if (sky::shell::AttemptLaunchFromCommandLineSwitches(_engine)) {
    return;
  }

  UIAlertView* alert = [[UIAlertView alloc]
          initWithTitle:@"Error"
                message:@"Could not resolve one or all of either the main dart "
                        @"file path, the FLX bundle path or the package root "
                        @"on the host. Use the tooling to relaunch the "
                        @"application."
               delegate:self
      cancelButtonTitle:@"OK"
      otherButtonTitles:nil];
  [alert show];
  [alert release];
}

299
#else
300 301 302 303 304

- (void)runFromPrecompiledSource {
  mojo::String bundle_path([self flxBundlePath]);
  CHECK(bundle_path.size() != 0)
      << "There must be a valid FLX bundle to run the application";
305
  _engine->RunFromPrecompiledSnapshot(bundle_path);
C
Chinmay Garde 已提交
306 307
}

308 309
#endif  // TARGET_IPHONE_SIMULATOR

C
Chinmay Garde 已提交
310 311 312
#pragma mark - UIResponder overrides for raw touches

- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase {
313
  auto eventTypePhase = PointerTypePhaseFromUITouchPhase(phase);
C
Chinmay Garde 已提交
314
  const CGFloat scale = [UIScreen mainScreen].scale;
315
  auto pointer_packet = pointer::PointerPacket::New();
C
Chinmay Garde 已提交
316 317

  for (UITouch* touch in touches) {
318 319 320 321 322 323 324 325 326 327 328
    int touch_identifier = 0;
    uintptr_t touch_ptr = reinterpret_cast<uintptr_t>(touch);

    switch (eventTypePhase.second) {
      case Accessed:
        touch_identifier = _touch_mapper.identifierOf(touch_ptr);
        break;
      case Added:
        touch_identifier = _touch_mapper.registerTouch(touch_ptr);
        break;
      case Removed:
329
        touch_identifier = _touch_mapper.unregisterTouch(touch_ptr);
330 331 332
        break;
    }
    DCHECK(touch_identifier != 0);
C
Chinmay Garde 已提交
333
    CGPoint windowCoordinates = [touch locationInView:nil];
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    auto pointer_time = InputEventTimestampFromNSTimeInterval(touch.timestamp);

    auto pointer_data = pointer::Pointer::New();

    pointer_data->time_stamp = pointer_time;
    pointer_data->type = eventTypePhase.first;
    pointer_data->kind = pointer::PointerKind::TOUCH;
    pointer_data->pointer = touch_identifier;
    pointer_data->x = windowCoordinates.x * scale;
    pointer_data->y = windowCoordinates.y * scale;
    pointer_data->buttons = 0;
    pointer_data->down = false;
    pointer_data->primary = false;
    pointer_data->obscured = false;
    pointer_data->pressure = 1.0;
    pointer_data->pressure_min = 0.0;
    pointer_data->pressure_max = 1.0;
    pointer_data->distance = 0.0;
    pointer_data->distance_min = 0.0;
    pointer_data->distance_max = 0.0;
    pointer_data->radius_major = 0.0;
    pointer_data->radius_minor = 0.0;
    pointer_data->radius_min = 0.0;
    pointer_data->radius_max = 0.0;
    pointer_data->orientation = 0.0;
    pointer_data->tilt = 0.0;

    pointer_packet->pointers.push_back(pointer_data.Pass());
C
Chinmay Garde 已提交
362
  }
363

364
  _engine->OnPointerPacket(pointer_packet.Pass());
C
Chinmay Garde 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
  [self dispatchTouches:touches phase:UITouchPhaseBegan];
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
  [self dispatchTouches:touches phase:UITouchPhaseMoved];
}

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
  [self dispatchTouches:touches phase:UITouchPhaseEnded];
}

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
  [self dispatchTouches:touches phase:UITouchPhaseCancelled];
}

383 384 385 386 387 388
#pragma mark - Input Clicks

- (BOOL)enableInputClicksWhenVisible {
  return YES;
}

389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
#pragma mark - Surface Lifecycle

- (void)notifySurfaceCreation {
  TRACE_EVENT0("flutter", "notifySurfaceCreation");
  self.platformView->SurfaceCreated(self.acceleratedWidget);
}

- (void)notifySurfaceDestruction {
  TRACE_EVENT0("flutter", "notifySurfaceDestruction");
  self.platformView->SurfaceDestroyed();
}

- (void)visibilityDidChange:(BOOL)visible {
  if (visible) {
    [self notifySurfaceCreation];
  } else {
    [self notifySurfaceDestruction];
  }
}

- (void)applicationBecameActive:(NSNotification*)notification {
A
Adam Barth 已提交
410 411
  if (_engine) {
    _engine->OnAppLifecycleStateChanged(sky::AppLifecycleState::RESUMED);
412 413 414 415
  }
}

- (void)applicationWillResignActive:(NSNotification*)notification {
A
Adam Barth 已提交
416 417
  if (_engine) {
    _engine->OnAppLifecycleStateChanged(sky::AppLifecycleState::PAUSED);
418 419 420
  }
}

C
Chinmay Garde 已提交
421 422 423 424 425 426 427
#pragma mark - Misc.

+ (Class)layerClass {
  return [CAEAGLLayer class];
}

- (void)dealloc {
428
  [_dynamic_service_loader release];
429
  [self notifySurfaceDestruction];
430
  [[NSNotificationCenter defaultCenter] removeObserver:self];
C
Chinmay Garde 已提交
431 432 433 434
  [super dealloc];
}

@end