sky_surface.mm 11.3 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/logging.h"
12
#include "base/time/time.h"
C
Chinmay Garde 已提交
13
#include "base/trace_event/trace_event.h"
14 15
#include "mojo/public/cpp/bindings/interface_request.h"
#include "sky/services/engine/input_event.mojom.h"
16
#include "sky/services/pointer/pointer.mojom.h"
17
#include "sky/shell/platform/mac/platform_service_provider.h"
18
#include "sky/shell/platform/mac/platform_view_mac.h"
19
#include "sky/shell/shell.h"
20
#include "sky/shell/shell_view.h"
21
#include "sky/shell/ui_delegate.h"
22
#include <strings.h>
C
Chinmay Garde 已提交
23

24 25 26 27 28 29
enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

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

51 52
  return PointerTypeMapperPhase(pointer::PointerType::CANCEL,
                                MapperPhase::Accessed);
C
Chinmay Garde 已提交
53 54
}

55 56
static inline int64 InputEventTimestampFromNSTimeInterval(
    NSTimeInterval interval) {
A
Adam Barth 已提交
57
  return base::TimeDelta::FromSecondsD(interval).InMicroseconds();
58 59
}

60 61 62 63 64 65 66 67 68 69 70 71 72 73
// 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;
  }

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

  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_;
};

89 90 91 92
@interface SkySurface ()<UIInputViewAudioFeedback>

@end

C
Chinmay Garde 已提交
93 94
@implementation SkySurface {
  BOOL _platformViewInitialized;
95
  CGPoint _lastScrollTranslation;
96
  sky::ViewportMetricsPtr _viewportMetrics;
C
Chinmay Garde 已提交
97

98
  sky::SkyEnginePtr _engine;
99
  std::unique_ptr<sky::shell::ShellView> _shell_view;
100
  TouchMapper _touch_mapper;
101 102
}

103
static std::string TracesBasePath() {
104 105
  NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                       NSUserDomainMask, YES);
106
  return [paths.firstObject UTF8String];
107 108
}

109
- (instancetype)initWithShellView:(sky::shell::ShellView*)shellView {
C
Chinmay Garde 已提交
110
  TRACE_EVENT0("flutter", "initWithShellView");
111
  self = [super init];
112
  if (self) {
113 114
    _viewportMetrics = sky::ViewportMetrics::New();

115 116
    base::FilePath tracesPath =
        base::FilePath::FromUTF8Unsafe(TracesBasePath());
117 118
    sky::shell::Shell::Shared().tracing_controller().set_traces_base_path(
        tracesPath);
119

120
    _shell_view.reset(shellView);
121
    self.multipleTouchEnabled = YES;
122 123 124 125 126 127 128 129 130 131 132 133

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

    [[NSNotificationCenter defaultCenter]
        addObserver:self
           selector:@selector(applicationWillResignActive:)
               name:UIApplicationWillResignActiveNotification
             object:nil];
134 135 136 137 138 139 140 141

    [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(keyboardWasShown:)
        name:UIKeyboardDidShowNotification object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
         selector:@selector(keyboardWillBeHidden:)
         name:UIKeyboardWillHideNotification object:nil];
142
  }
143
  return self;
C
Chinmay Garde 已提交
144 145 146 147 148 149
}

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

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
- (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());
}

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

C
Chinmay Garde 已提交
166
- (void)layoutSubviews {
C
Chinmay Garde 已提交
167
  TRACE_EVENT0("flutter", "layoutSubviews");
C
Chinmay Garde 已提交
168 169 170 171 172 173 174 175 176
  [super layoutSubviews];

  [self configureLayerDefaults];

  [self setupPlatformViewIfNecessary];

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

177 178 179 180
  _viewportMetrics->device_pixel_ratio = scale;
  _viewportMetrics->physical_width = size.width * scale;
  _viewportMetrics->physical_height = size.height * scale;
  _viewportMetrics->physical_padding_top =
181
      [UIApplication sharedApplication].statusBarFrame.size.height * scale;
182

183
  _engine->OnViewportMetricsChanged(_viewportMetrics.Clone());
C
Chinmay Garde 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
}

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

203
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
204 205
}

206 207
- (sky::shell::PlatformViewMac*)platformView {
  auto view = static_cast<sky::shell::PlatformViewMac*>(_shell_view->view());
C
Chinmay Garde 已提交
208 209 210 211
  DCHECK(view);
  return view;
}

212
- (const char*)flxBundlePath {
213 214 215 216 217
  // 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.
  // TODO: Allow specification of the application bundle identifier
  NSBundle* applicationBundle = [NSBundle
218
      bundleWithIdentifier:@"io.flutter.application.FlutterApplication"];
219 220 221 222 223 224 225 226
  NSString* path = [applicationBundle pathForResource:@"app" ofType:@"flx"];
  if (path.length != 0) {
    return path.UTF8String;
  }
  return
      [[NSBundle mainBundle] pathForResource:@"app" ofType:@"flx"].UTF8String;
}

227
- (void)connectToEngineAndLoad {
C
Chinmay Garde 已提交
228
  TRACE_EVENT0("flutter", "connectToEngineAndLoad");
229
  self.platformView->ConnectToEngine(mojo::GetProxy(&_engine));
230 231 232 233 234

  mojo::ServiceProviderPtr service_provider;
  new sky::shell::PlatformServiceProvider(mojo::GetProxy(&service_provider));
  sky::ServicesDataPtr services = sky::ServicesData::New();
  services->services_provided_by_embedder = service_provider.Pass();
235
  _engine->SetServices(services.Pass());
236

237
  mojo::String bundle_path([self flxBundlePath]);
238

239 240 241
  CHECK(bundle_path.size() != 0)
      << "There must be a valid FLX bundle to run the application";

242
#if TARGET_IPHONE_SIMULATOR
243
  _engine->RunFromBundle(bundle_path);
244
#else
245
  _engine->RunFromPrecompiledSnapshot(bundle_path);
246
#endif
C
Chinmay Garde 已提交
247 248 249 250 251
}

#pragma mark - UIResponder overrides for raw touches

- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase {
252
  auto eventTypePhase = PointerTypePhaseFromUITouchPhase(phase);
C
Chinmay Garde 已提交
253
  const CGFloat scale = [UIScreen mainScreen].scale;
254
  auto pointer_packet = pointer::PointerPacket::New();
C
Chinmay Garde 已提交
255 256

  for (UITouch* touch in touches) {
257 258 259 260 261 262 263 264 265 266 267
    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:
268
        touch_identifier = _touch_mapper.unregisterTouch(touch_ptr);
269 270 271
        break;
    }
    DCHECK(touch_identifier != 0);
C
Chinmay Garde 已提交
272
    CGPoint windowCoordinates = [touch locationInView:nil];
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
    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 已提交
301
  }
302

303
  _engine->OnPointerPacket(pointer_packet.Pass());
C
Chinmay Garde 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
}

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

322 323 324 325 326 327
#pragma mark - Input Clicks

- (BOOL)enableInputClicksWhenVisible {
  return YES;
}

328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
#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 已提交
349 350
  if (_engine) {
    _engine->OnAppLifecycleStateChanged(sky::AppLifecycleState::RESUMED);
351 352 353 354
  }
}

- (void)applicationWillResignActive:(NSNotification*)notification {
A
Adam Barth 已提交
355 356
  if (_engine) {
    _engine->OnAppLifecycleStateChanged(sky::AppLifecycleState::PAUSED);
357 358 359
  }
}

C
Chinmay Garde 已提交
360 361 362 363 364 365 366
#pragma mark - Misc.

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

- (void)dealloc {
367
  [self notifySurfaceDestruction];
368
  [[NSNotificationCenter defaultCenter] removeObserver:self];
C
Chinmay Garde 已提交
369 370 371 372
  [super dealloc];
}

@end