sky_surface.mm 12.8 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
  return self;
C
Chinmay Garde 已提交
162 163 164 165 166 167
}

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

168 169 170 171 172 173 174
- (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());
175 176
}

177 178 179
- (void)keyboardWillBeHidden:(NSNotification*)notification {
  _viewportMetrics->physical_padding_bottom = 0.0;
  _engine->OnViewportMetricsChanged(_viewportMetrics.Clone());
180 181
}

C
Chinmay Garde 已提交
182
- (void)layoutSubviews {
C
Chinmay Garde 已提交
183
  TRACE_EVENT0("flutter", "layoutSubviews");
C
Chinmay Garde 已提交
184 185 186 187 188 189 190 191 192
  [super layoutSubviews];

  [self configureLayerDefaults];

  [self setupPlatformViewIfNecessary];

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

193 194 195 196
  _viewportMetrics->device_pixel_ratio = scale;
  _viewportMetrics->physical_width = size.width * scale;
  _viewportMetrics->physical_height = size.height * scale;
  _viewportMetrics->physical_padding_top =
197
      [UIApplication sharedApplication].statusBarFrame.size.height * scale;
198

199
  _engine->OnViewportMetricsChanged(_viewportMetrics.Clone());
C
Chinmay Garde 已提交
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
}

- (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;

219
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
220 221
}

222 223
- (sky::shell::PlatformViewMac*)platformView {
  auto view = static_cast<sky::shell::PlatformViewMac*>(_shell_view->view());
C
Chinmay Garde 已提交
224 225 226 227
  DCHECK(view);
  return view;
}

228
- (const char*)flxBundlePath {
229 230 231 232
  // 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
233
      bundleWithIdentifier:@"io.flutter.application.FlutterApplication"];
234 235 236 237 238 239 240 241
  NSString* path = [applicationBundle pathForResource:@"app" ofType:@"flx"];
  if (path.length != 0) {
    return path.UTF8String;
  }
  return
      [[NSBundle mainBundle] pathForResource:@"app" ofType:@"flx"].UTF8String;
}

242
- (void)connectToEngineAndLoad {
C
Chinmay Garde 已提交
243
  TRACE_EVENT0("flutter", "connectToEngineAndLoad");
244
  self.platformView->ConnectToEngine(mojo::GetProxy(&_engine));
245

246 247
  _dynamic_service_loader = [[SkyDynamicServiceLoader alloc] init];
  void* baton = _dynamic_service_loader;
248
  mojo::ServiceProviderPtr service_provider;
249 250 251
  new sky::shell::PlatformServiceProvider(
      mojo::GetProxy(&service_provider),
      base::Bind(&DynamicServiceResolve, base::Unretained(baton)));
252 253
  sky::ServicesDataPtr services = sky::ServicesData::New();
  services->services_provided_by_embedder = service_provider.Pass();
254
  _engine->SetServices(services.Pass());
255

256 257 258 259 260 261
#if TARGET_IPHONE_SIMULATOR
  [self runFromDartSource];
#else
  [self runFromPrecompiledSource];
#endif
}
262

263
#if TARGET_IPHONE_SIMULATOR
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282

- (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];
}

283
#else
284 285 286 287 288

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

292 293
#endif  // TARGET_IPHONE_SIMULATOR

C
Chinmay Garde 已提交
294 295 296
#pragma mark - UIResponder overrides for raw touches

- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase {
297
  auto eventTypePhase = PointerTypePhaseFromUITouchPhase(phase);
C
Chinmay Garde 已提交
298
  const CGFloat scale = [UIScreen mainScreen].scale;
299
  auto pointer_packet = pointer::PointerPacket::New();
C
Chinmay Garde 已提交
300 301

  for (UITouch* touch in touches) {
302 303 304 305 306 307 308 309 310 311 312
    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:
313
        touch_identifier = _touch_mapper.unregisterTouch(touch_ptr);
314 315 316
        break;
    }
    DCHECK(touch_identifier != 0);
C
Chinmay Garde 已提交
317
    CGPoint windowCoordinates = [touch locationInView:nil];
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
    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 已提交
346
  }
347

348
  _engine->OnPointerPacket(pointer_packet.Pass());
C
Chinmay Garde 已提交
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
}

- (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];
}

367 368 369 370 371 372
#pragma mark - Input Clicks

- (BOOL)enableInputClicksWhenVisible {
  return YES;
}

373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
#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 已提交
394 395
  if (_engine) {
    _engine->OnAppLifecycleStateChanged(sky::AppLifecycleState::RESUMED);
396 397 398 399
  }
}

- (void)applicationWillResignActive:(NSNotification*)notification {
A
Adam Barth 已提交
400 401
  if (_engine) {
    _engine->OnAppLifecycleStateChanged(sky::AppLifecycleState::PAUSED);
402 403 404
  }
}

C
Chinmay Garde 已提交
405 406 407 408 409 410 411
#pragma mark - Misc.

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

- (void)dealloc {
412
  [_dynamic_service_loader release];
413
  [self notifySurfaceDestruction];
414
  [[NSNotificationCenter defaultCenter] removeObserver:self];
C
Chinmay Garde 已提交
415 416 417 418
  [super dealloc];
}

@end