| Index: remoting/ios/ui/host_view_controller.mm
|
| diff --git a/remoting/ios/ui/host_view_controller.mm b/remoting/ios/ui/host_view_controller.mm
|
| deleted file mode 100644
|
| index d87e7674bdc9d29e499e1d9073299db118e45c1e..0000000000000000000000000000000000000000
|
| --- a/remoting/ios/ui/host_view_controller.mm
|
| +++ /dev/null
|
| @@ -1,676 +0,0 @@
|
| -// Copyright 2014 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.
|
| -
|
| -#if !defined(__has_feature) || !__has_feature(objc_arc)
|
| -#error "This file requires ARC support."
|
| -#endif
|
| -
|
| -#import "remoting/ios/ui/host_view_controller.h"
|
| -
|
| -#include <OpenGLES/ES2/gl.h>
|
| -
|
| -#import "remoting/ios/data_store.h"
|
| -
|
| -namespace {
|
| -
|
| -// TODO (aboone) Some of the layout is not yet set in stone, so variables have
|
| -// been used to position and turn items on and off. Eventually these may be
|
| -// stabilized and removed.
|
| -
|
| -// Scroll speed multiplier for mouse wheel
|
| -const static int kMouseWheelSensitivity = 20;
|
| -
|
| -// Area the navigation bar consumes when visible in pixels
|
| -const static int kTopMargin = 20;
|
| -// Area the footer consumes when visible (no footer currently exists)
|
| -const static int kBottomMargin = 0;
|
| -
|
| -} // namespace
|
| -
|
| -@interface HostViewController (Private)
|
| -- (void)setupGL;
|
| -- (void)tearDownGL;
|
| -- (void)goBack;
|
| -- (void)updateLabels;
|
| -- (BOOL)isToolbarHidden;
|
| -- (void)updatePanVelocityShouldCancel:(bool)canceled;
|
| -- (void)orientationChanged:(NSNotification*)note;
|
| -- (void)applySceneChange:(CGPoint)translation scaleBy:(float)ratio;
|
| -- (void)showToolbar:(BOOL)visible;
|
| -@end
|
| -
|
| -@implementation HostViewController
|
| -
|
| -@synthesize host = _host;
|
| -@synthesize userEmail = _userEmail;
|
| -@synthesize userAuthorizationToken = _userAuthorizationToken;
|
| -
|
| -// Override UIViewController
|
| -- (void)viewDidLoad {
|
| - [super viewDidLoad];
|
| -
|
| - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
| - DCHECK(_context);
|
| - static_cast<GLKView*>(self.view).context = _context;
|
| -
|
| - [_keyEntryView setDelegate:self];
|
| -
|
| - _clientToHostProxy = [[HostProxy alloc] init];
|
| -
|
| - // There is a 1 pixel top border which is actually the background not being
|
| - // covered. There is no obvious way to remove that pixel 'border'. Set the
|
| - // background clear, and also reset the backgroundimage and shawdowimage to an
|
| - // empty image any time the view is moved.
|
| - _hiddenToolbar.backgroundColor = [UIColor clearColor];
|
| - if ([_hiddenToolbar respondsToSelector:@selector(setBackgroundImage:
|
| - forToolbarPosition:
|
| - barMetrics:)]) {
|
| - [_hiddenToolbar setBackgroundImage:[UIImage new]
|
| - forToolbarPosition:UIToolbarPositionAny
|
| - barMetrics:UIBarMetricsDefault];
|
| - }
|
| - if ([_hiddenToolbar
|
| - respondsToSelector:@selector(setShadowImage:forToolbarPosition:)]) {
|
| - [_hiddenToolbar setShadowImage:[UIImage new]
|
| - forToolbarPosition:UIToolbarPositionAny];
|
| - }
|
| -
|
| - // 1/2 circle rotation for an icon ~ 180 degree ~ 1 radian
|
| - _barBtnNavigation.imageView.transform = CGAffineTransformMakeRotation(M_PI);
|
| -
|
| - _scene = [[SceneView alloc] init];
|
| - [_scene setMarginsFromLeft:0 right:0 top:kTopMargin bottom:kBottomMargin];
|
| - _desktop = [[DesktopTexture alloc] init];
|
| - _mouse = [[CursorTexture alloc] init];
|
| -
|
| - _glBufferLock = [[NSLock alloc] init];
|
| - _glCursorLock = [[NSLock alloc] init];
|
| -
|
| - [_scene
|
| - setContentSize:[Utility getOrientatedSize:self.view.bounds.size
|
| - shouldWidthBeLongestSide:[Utility isInLandscapeMode]]];
|
| - [self showToolbar:YES];
|
| - [self updateLabels];
|
| -
|
| - [self setupGL];
|
| -
|
| - [_singleTapRecognizer requireGestureRecognizerToFail:_twoFingerTapRecognizer];
|
| - [_twoFingerTapRecognizer
|
| - requireGestureRecognizerToFail:_threeFingerTapRecognizer];
|
| - //[_pinchRecognizer requireGestureRecognizerToFail:_twoFingerTapRecognizer];
|
| - [_panRecognizer requireGestureRecognizerToFail:_singleTapRecognizer];
|
| - [_threeFingerPanRecognizer
|
| - requireGestureRecognizerToFail:_threeFingerTapRecognizer];
|
| - //[_pinchRecognizer requireGestureRecognizerToFail:_threeFingerPanRecognizer];
|
| -
|
| - // Subscribe to changes in orientation
|
| - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
|
| - [[NSNotificationCenter defaultCenter]
|
| - addObserver:self
|
| - selector:@selector(orientationChanged:)
|
| - name:UIDeviceOrientationDidChangeNotification
|
| - object:[UIDevice currentDevice]];
|
| -}
|
| -
|
| -- (void)setupGL {
|
| - [EAGLContext setCurrentContext:_context];
|
| -
|
| - _effect = [[GLKBaseEffect alloc] init];
|
| - [Utility logGLErrorCode:@"setupGL begin"];
|
| -
|
| - // Initialize each texture
|
| - [_desktop bindToEffect:[_effect texture2d0]];
|
| - [_mouse bindToEffect:[_effect texture2d1]];
|
| - [Utility logGLErrorCode:@"setupGL textureComplete"];
|
| -}
|
| -
|
| -// Override UIViewController
|
| -- (void)viewDidUnload {
|
| - [super viewDidUnload];
|
| - [self tearDownGL];
|
| -
|
| - if ([EAGLContext currentContext] == _context) {
|
| - [EAGLContext setCurrentContext:nil];
|
| - }
|
| - _context = nil;
|
| -}
|
| -
|
| -- (void)tearDownGL {
|
| - [EAGLContext setCurrentContext:_context];
|
| -
|
| - // Release Textures
|
| - [_desktop releaseTexture];
|
| - [_mouse releaseTexture];
|
| -}
|
| -
|
| -// Override UIViewController
|
| -- (void)viewWillAppear:(BOOL)animated {
|
| - [super viewWillAppear:NO];
|
| - [self.navigationController setNavigationBarHidden:YES animated:YES];
|
| - [self updateLabels];
|
| - if (![_clientToHostProxy isConnected]) {
|
| - [_busyIndicator startAnimating];
|
| -
|
| - [_clientToHostProxy connectToHost:_userEmail
|
| - authToken:_userAuthorizationToken
|
| - jabberId:_host.jabberId
|
| - hostId:_host.hostId
|
| - publicKey:_host.publicKey
|
| - delegate:self];
|
| - }
|
| -}
|
| -
|
| -// Override UIViewController
|
| -- (void)viewWillDisappear:(BOOL)animated {
|
| - [super viewWillDisappear:NO];
|
| - NSArray* viewControllers = self.navigationController.viewControllers;
|
| - if (viewControllers.count > 1 &&
|
| - [viewControllers objectAtIndex:viewControllers.count - 2] == self) {
|
| - // View is disappearing because a new view controller was pushed onto the
|
| - // stack
|
| - } else if ([viewControllers indexOfObject:self] == NSNotFound) {
|
| - // View is disappearing because it was popped from the stack
|
| - [_clientToHostProxy disconnectFromHost];
|
| - }
|
| -}
|
| -
|
| -// "Back" goes to the root controller for now
|
| -- (void)goBack {
|
| - [self.navigationController popToRootViewControllerAnimated:YES];
|
| -}
|
| -
|
| -// @protocol PinEntryViewControllerDelegate
|
| -// Return the PIN input by User, indicate if the User should be prompted to
|
| -// re-enter the pin in the future
|
| -- (void)connectToHostWithPin:(UIViewController*)controller
|
| - hostPin:(NSString*)hostPin
|
| - shouldPrompt:(BOOL)shouldPrompt {
|
| - const HostPreferences* hostPrefs =
|
| - [[DataStore sharedStore] getHostForId:_host.hostId];
|
| - if (!hostPrefs) {
|
| - hostPrefs = [[DataStore sharedStore] createHost:_host.hostId];
|
| - }
|
| - if (hostPrefs) {
|
| - hostPrefs.hostPin = hostPin;
|
| - hostPrefs.askForPin = [NSNumber numberWithBool:shouldPrompt];
|
| - [[DataStore sharedStore] saveChanges];
|
| - }
|
| -
|
| - [[controller presentingViewController] dismissViewControllerAnimated:NO
|
| - completion:nil];
|
| -
|
| - [_clientToHostProxy authenticationResponse:hostPin createPair:!shouldPrompt];
|
| -}
|
| -
|
| -// @protocol PinEntryViewControllerDelegate
|
| -// Returns if the user canceled while entering their PIN
|
| -- (void)cancelledConnectToHostWithPin:(UIViewController*)controller {
|
| - [[controller presentingViewController] dismissViewControllerAnimated:NO
|
| - completion:nil];
|
| -
|
| - [self goBack];
|
| -}
|
| -
|
| -- (void)setHostDetails:(Host*)host
|
| - userEmail:(NSString*)userEmail
|
| - authorizationToken:(NSString*)authorizationToken {
|
| - DCHECK(host.jabberId);
|
| - _host = host;
|
| - _userEmail = userEmail;
|
| - _userAuthorizationToken = authorizationToken;
|
| -}
|
| -
|
| -// Set various labels on the form for iPad vs iPhone, and orientation
|
| -- (void)updateLabels {
|
| - if (![Utility isPad] && ![Utility isInLandscapeMode]) {
|
| - [_barBtnDisconnect setTitle:@"" forState:(UIControlStateNormal)];
|
| - [_barBtnCtrlAltDel setTitle:@"CtAtD" forState:UIControlStateNormal];
|
| - } else {
|
| - [_barBtnCtrlAltDel setTitle:@"Ctrl+Alt+Del" forState:UIControlStateNormal];
|
| -
|
| - NSString* hostStatus = _host.hostName;
|
| - if (![_statusMessage isEqual:@"Connected"]) {
|
| - hostStatus = [NSString
|
| - stringWithFormat:@"%@ - %@", _host.hostName, _statusMessage];
|
| - }
|
| - [_barBtnDisconnect setTitle:hostStatus forState:UIControlStateNormal];
|
| - }
|
| -
|
| - [_barBtnDisconnect sizeToFit];
|
| - [_barBtnCtrlAltDel sizeToFit];
|
| -}
|
| -
|
| -// Resize the view of the desktop - Zoom in/out. This can occur during a Pan.
|
| -- (IBAction)pinchGestureTriggered:(UIPinchGestureRecognizer*)sender {
|
| - if ([sender state] == UIGestureRecognizerStateChanged) {
|
| - [self applySceneChange:CGPointMake(0.0, 0.0) scaleBy:sender.scale];
|
| -
|
| - sender.scale = 1.0; // reset scale so next iteration is a relative ratio
|
| - }
|
| -}
|
| -
|
| -- (IBAction)tapGestureTriggered:(UITapGestureRecognizer*)sender {
|
| - if ([_scene containsTouchPoint:[sender locationInView:self.view]]) {
|
| - [Utility leftClickOn:_clientToHostProxy at:_scene.mousePosition];
|
| - }
|
| -}
|
| -
|
| -// Change position of scene. This can occur during a pinch or longpress.
|
| -// Or perform a Mouse Wheel Scroll
|
| -- (IBAction)panGestureTriggered:(UIPanGestureRecognizer*)sender {
|
| - CGPoint translation = [sender translationInView:self.view];
|
| -
|
| - // If we start with 2 touches, and the pinch gesture is not in progress yet,
|
| - // then disable it, so mouse scrolling and zoom do not occur at the same
|
| - // time.
|
| - if ([sender numberOfTouches] == 2 &&
|
| - [sender state] == UIGestureRecognizerStateBegan &&
|
| - !(_pinchRecognizer.state == UIGestureRecognizerStateBegan ||
|
| - _pinchRecognizer.state == UIGestureRecognizerStateChanged)) {
|
| - _pinchRecognizer.enabled = NO;
|
| - }
|
| -
|
| - if (!_pinchRecognizer.enabled) {
|
| - // Began with 2 touches, so this is a scroll event
|
| - translation.x *= kMouseWheelSensitivity;
|
| - translation.y *= kMouseWheelSensitivity;
|
| - [Utility mouseScroll:_clientToHostProxy
|
| - at:_scene.mousePosition
|
| - delta:webrtc::DesktopVector(translation.x, translation.y)];
|
| - } else {
|
| - // Did not begin with 2 touches, doing a pan event
|
| - if ([sender state] == UIGestureRecognizerStateChanged) {
|
| - CGPoint translation = [sender translationInView:self.view];
|
| -
|
| - [self applySceneChange:translation scaleBy:1.0];
|
| -
|
| - } else if ([sender state] == UIGestureRecognizerStateEnded) {
|
| - // After user removes their fingers from the screen, apply an acceleration
|
| - // effect
|
| - [_scene setPanVelocity:[sender velocityInView:self.view]];
|
| - }
|
| - }
|
| -
|
| - // Finished the event chain
|
| - if (!([sender state] == UIGestureRecognizerStateBegan ||
|
| - [sender state] == UIGestureRecognizerStateChanged)) {
|
| - _pinchRecognizer.enabled = YES;
|
| - }
|
| -
|
| - // Reset translation so next iteration is relative.
|
| - [sender setTranslation:CGPointZero inView:self.view];
|
| -}
|
| -
|
| -// Click-Drag mouse operation. This can occur during a Pan.
|
| -- (IBAction)longPressGestureTriggered:(UILongPressGestureRecognizer*)sender {
|
| -
|
| - if ([sender state] == UIGestureRecognizerStateBegan) {
|
| - [_clientToHostProxy mouseAction:_scene.mousePosition
|
| - wheelDelta:webrtc::DesktopVector(0, 0)
|
| - whichButton:1
|
| - buttonDown:YES];
|
| - } else if (!([sender state] == UIGestureRecognizerStateBegan ||
|
| - [sender state] == UIGestureRecognizerStateChanged)) {
|
| - [_clientToHostProxy mouseAction:_scene.mousePosition
|
| - wheelDelta:webrtc::DesktopVector(0, 0)
|
| - whichButton:1
|
| - buttonDown:NO];
|
| - }
|
| -}
|
| -
|
| -- (IBAction)twoFingerTapGestureTriggered:(UITapGestureRecognizer*)sender {
|
| - if ([_scene containsTouchPoint:[sender locationInView:self.view]]) {
|
| - [Utility rightClickOn:_clientToHostProxy at:_scene.mousePosition];
|
| - }
|
| -}
|
| -
|
| -- (IBAction)threeFingerTapGestureTriggered:(UITapGestureRecognizer*)sender {
|
| -
|
| - if ([_scene containsTouchPoint:[sender locationInView:self.view]]) {
|
| - [Utility middleClickOn:_clientToHostProxy at:_scene.mousePosition];
|
| - }
|
| -}
|
| -
|
| -- (IBAction)threeFingerPanGestureTriggered:(UIPanGestureRecognizer*)sender {
|
| - if ([sender state] == UIGestureRecognizerStateChanged) {
|
| - CGPoint translation = [sender translationInView:self.view];
|
| - if (translation.y > 0) {
|
| - // Swiped down
|
| - [self showToolbar:YES];
|
| - } else if (translation.y < 0) {
|
| - // Swiped up
|
| - [_keyEntryView becomeFirstResponder];
|
| - [self updateLabels];
|
| - }
|
| - [sender setTranslation:CGPointZero inView:self.view];
|
| - }
|
| -}
|
| -
|
| -- (IBAction)barBtnNavigationBackPressed:(id)sender {
|
| - [self goBack];
|
| -}
|
| -
|
| -- (IBAction)barBtnKeyboardPressed:(id)sender {
|
| - if ([_keyEntryView isFirstResponder]) {
|
| - [_keyEntryView endEditing:NO];
|
| - } else {
|
| - [_keyEntryView becomeFirstResponder];
|
| - }
|
| -
|
| - [self updateLabels];
|
| -}
|
| -
|
| -- (IBAction)barBtnToolBarHidePressed:(id)sender {
|
| - [self showToolbar:[self isToolbarHidden]]; // Toolbar is either on
|
| - // screen or off screen
|
| -}
|
| -
|
| -- (IBAction)barBtnCtrlAltDelPressed:(id)sender {
|
| - [_keyEntryView ctrlAltDel];
|
| -}
|
| -
|
| -// Override UIResponder
|
| -// When any gesture begins, remove any acceleration effects currently being
|
| -// applied. Example, Panning view and let it shoot off into the distance, but
|
| -// then I see a spot I'm interested in so I will touch to capture that locations
|
| -// focus.
|
| -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
| - [self updatePanVelocityShouldCancel:YES];
|
| - [super touchesBegan:touches withEvent:event];
|
| -}
|
| -
|
| -// @protocol UIGestureRecognizerDelegate
|
| -// Allow panning and zooming to occur simultaneously.
|
| -// Allow panning and long press to occur simultaneously.
|
| -// Pinch requires 2 touches, and long press requires a single touch, so they are
|
| -// mutually exclusive regardless of if panning is the initiating gesture
|
| -- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
|
| - shouldRecognizeSimultaneouslyWithGestureRecognizer:
|
| - (UIGestureRecognizer*)otherGestureRecognizer {
|
| - if (gestureRecognizer == _pinchRecognizer ||
|
| - (gestureRecognizer == _panRecognizer)) {
|
| - if (otherGestureRecognizer == _pinchRecognizer ||
|
| - otherGestureRecognizer == _panRecognizer) {
|
| - return YES;
|
| - }
|
| - }
|
| -
|
| - if (gestureRecognizer == _longPressRecognizer ||
|
| - gestureRecognizer == _panRecognizer) {
|
| - if (otherGestureRecognizer == _longPressRecognizer ||
|
| - otherGestureRecognizer == _panRecognizer) {
|
| - return YES;
|
| - }
|
| - }
|
| - return NO;
|
| -}
|
| -
|
| -// @protocol ClientControllerDelegate
|
| -// Prompt the user for their PIN if pairing has not already been established
|
| -- (void)requestHostPin:(BOOL)pairingSupported {
|
| - BOOL requestPin = YES;
|
| - const HostPreferences* hostPrefs =
|
| - [[DataStore sharedStore] getHostForId:_host.hostId];
|
| - if (hostPrefs) {
|
| - requestPin = [hostPrefs.askForPin boolValue];
|
| - if (!requestPin) {
|
| - if (hostPrefs.hostPin == nil || hostPrefs.hostPin.length == 0) {
|
| - requestPin = YES;
|
| - }
|
| - }
|
| - }
|
| - if (requestPin == YES) {
|
| - PinEntryViewController* pinEntry = [[PinEntryViewController alloc] init];
|
| - [pinEntry setDelegate:self];
|
| - [pinEntry setHostName:_host.hostName];
|
| - [pinEntry setShouldPrompt:YES];
|
| - [pinEntry setPairingSupported:pairingSupported];
|
| -
|
| - [self presentViewController:pinEntry animated:YES completion:nil];
|
| - } else {
|
| - [_clientToHostProxy authenticationResponse:hostPrefs.hostPin
|
| - createPair:pairingSupported];
|
| - }
|
| -}
|
| -
|
| -// @protocol ClientControllerDelegate
|
| -// Occurs when a connection to a HOST is established successfully
|
| -- (void)connected {
|
| - // Everything is good, nothing to do
|
| -}
|
| -
|
| -// @protocol ClientControllerDelegate
|
| -- (void)connectionStatus:(NSString*)statusMessage {
|
| - _statusMessage = statusMessage;
|
| -
|
| - if ([_statusMessage isEqual:@"Connection closed"]) {
|
| - [self goBack];
|
| - } else {
|
| - [self updateLabels];
|
| - }
|
| -}
|
| -
|
| -// @protocol ClientControllerDelegate
|
| -// Occurs when a connection to a HOST has failed
|
| -- (void)connectionFailed:(NSString*)errorMessage {
|
| - [_busyIndicator stopAnimating];
|
| - NSString* errorMsg;
|
| - if ([_clientToHostProxy isConnected]) {
|
| - errorMsg = @"Lost Connection";
|
| - } else {
|
| - errorMsg = @"Unable to connect";
|
| - }
|
| - [Utility showAlert:errorMsg message:errorMessage];
|
| - [self goBack];
|
| -}
|
| -
|
| -// @protocol ClientControllerDelegate
|
| -// Copy the updated regions to a backing store to be consumed by the GL Context
|
| -// on a different thread. A region is stored in disjoint memory locations, and
|
| -// must be transformed to a contiguous memory buffer for a GL Texture write.
|
| -// /-----\
|
| -// | 2-4| This buffer is 5x3 bytes large, a region exists at bytes 2 to 4 and
|
| -// | 7-9| bytes 7 to 9. The region is extracted to a new contiguous buffer
|
| -// | | of 6 bytes in length.
|
| -// \-----/
|
| -// More than 1 region may exist in the frame from each call, in which case a new
|
| -// buffer is created for each region
|
| -- (void)applyFrame:(const webrtc::DesktopSize&)size
|
| - stride:(NSInteger)stride
|
| - data:(uint8_t*)data
|
| - regions:(const std::vector<webrtc::DesktopRect>&)regions {
|
| - [_glBufferLock lock]; // going to make changes to |_glRegions|
|
| -
|
| - if (!_scene.frameSize.equals(size)) {
|
| - // When this is the initial frame, the busyIndicator is still spinning. Now
|
| - // is a good time to stop it.
|
| - [_busyIndicator stopAnimating];
|
| -
|
| - // If the |_toolbar| is still showing, hide it.
|
| - [self showToolbar:NO];
|
| - [_scene setContentSize:
|
| - [Utility getOrientatedSize:self.view.bounds.size
|
| - shouldWidthBeLongestSide:[Utility isInLandscapeMode]]];
|
| - [_scene setFrameSize:size];
|
| - [_desktop setTextureSize:size];
|
| - [_mouse setTextureSize:size];
|
| - }
|
| -
|
| - uint32_t src_stride = stride;
|
| -
|
| - for (uint32_t i = 0; i < regions.size(); i++) {
|
| - scoped_ptr<GLRegion> region(new GLRegion());
|
| -
|
| - if (region.get()) {
|
| - webrtc::DesktopRect rect = regions.at(i);
|
| -
|
| - webrtc::DesktopSize(rect.width(), rect.height());
|
| - region->offset.reset(new webrtc::DesktopVector(rect.left(), rect.top()));
|
| - region->image.reset(new webrtc::BasicDesktopFrame(
|
| - webrtc::DesktopSize(rect.width(), rect.height())));
|
| -
|
| - if (region->image->data()) {
|
| - uint32_t bytes_per_row =
|
| - region->image->kBytesPerPixel * region->image->size().width();
|
| -
|
| - uint32_t offset =
|
| - (src_stride * region->offset->y()) + // row
|
| - (region->offset->x() * region->image->kBytesPerPixel); // column
|
| -
|
| - uint8_t* src_buffer = data + offset;
|
| - uint8_t* dst_buffer = region->image->data();
|
| -
|
| - // row by row copy
|
| - for (uint32_t j = 0; j < region->image->size().height(); j++) {
|
| - memcpy(dst_buffer, src_buffer, bytes_per_row);
|
| - dst_buffer += bytes_per_row;
|
| - src_buffer += src_stride;
|
| - }
|
| - _glRegions.push_back(region.release());
|
| - }
|
| - }
|
| - }
|
| - [_glBufferLock unlock]; // done making changes to |_glRegions|
|
| -}
|
| -
|
| -// @protocol ClientControllerDelegate
|
| -// Copy the delivered cursor to a backing store to be consumed by the GL Context
|
| -// on a different thread. Note only the most recent cursor is of importance,
|
| -// discard the previous cursor.
|
| -- (void)applyCursor:(const webrtc::DesktopSize&)size
|
| - hotspot:(const webrtc::DesktopVector&)hotspot
|
| - cursorData:(uint8_t*)data {
|
| -
|
| - [_glCursorLock lock]; // going to make changes to |_cursor|
|
| -
|
| - // MouseCursor takes ownership of DesktopFrame
|
| - [_mouse setCursor:new webrtc::MouseCursor(new webrtc::BasicDesktopFrame(size),
|
| - hotspot)];
|
| -
|
| - if (_mouse.cursor.image().data()) {
|
| - memcpy(_mouse.cursor.image().data(),
|
| - data,
|
| - size.width() * size.height() * _mouse.cursor.image().kBytesPerPixel);
|
| - } else {
|
| - [_mouse setCursor:NULL];
|
| - }
|
| -
|
| - [_glCursorLock unlock]; // done making changes to |_cursor|
|
| -}
|
| -
|
| -// @protocol GLKViewDelegate
|
| -// There is quite a few gotchas involved in working with this function. For
|
| -// sanity purposes, I've just assumed calls to the function are on a different
|
| -// thread which I've termed GL Context. Any variables consumed by this function
|
| -// should be thread safe.
|
| -//
|
| -// Clear Screen, update desktop, update cursor, define position, and finally
|
| -// present
|
| -//
|
| -// In general, avoid expensive work in this function to maximize frame rate.
|
| -- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
|
| - [self updatePanVelocityShouldCancel:NO];
|
| -
|
| - // Clear to black, to give the background color
|
| - glClearColor(0.0, 0.0, 0.0, 1.0);
|
| - glClear(GL_COLOR_BUFFER_BIT);
|
| -
|
| - [Utility logGLErrorCode:@"drawInRect bindBuffer"];
|
| -
|
| - if (_glRegions.size() > 0 || [_desktop needDraw]) {
|
| - [_glBufferLock lock];
|
| -
|
| - for (uint32_t i = 0; i < _glRegions.size(); i++) {
|
| - // |_glRegions[i].data| has been properly ordered by [self applyFrame]
|
| - [_desktop drawRegion:_glRegions[i] rect:rect];
|
| - }
|
| -
|
| - _glRegions.clear();
|
| - [_glBufferLock unlock];
|
| - }
|
| -
|
| - if ([_mouse needDrawAtPosition:_scene.mousePosition]) {
|
| - [_glCursorLock lock];
|
| - [_mouse drawWithMousePosition:_scene.mousePosition];
|
| - [_glCursorLock unlock];
|
| - }
|
| -
|
| - [_effect transform].projectionMatrix = _scene.projectionMatrix;
|
| - [_effect transform].modelviewMatrix = _scene.modelViewMatrix;
|
| - [_effect prepareToDraw];
|
| -
|
| - [Utility logGLErrorCode:@"drawInRect prepareToDrawComplete"];
|
| -
|
| - [_scene draw];
|
| -}
|
| -
|
| -// @protocol KeyInputDelegate
|
| -- (void)keyboardDismissed {
|
| - [self updateLabels];
|
| -}
|
| -
|
| -// @protocol KeyInputDelegate
|
| -// Send keyboard input to HOST
|
| -- (void)keyboardActionKeyCode:(uint32_t)keyPressed isKeyDown:(BOOL)keyDown {
|
| - [_clientToHostProxy keyboardAction:keyPressed keyDown:keyDown];
|
| -}
|
| -
|
| -- (BOOL)isToolbarHidden {
|
| - return (_toolbar.frame.origin.y < 0);
|
| -}
|
| -
|
| -// Update the scene acceleration vector
|
| -- (void)updatePanVelocityShouldCancel:(bool)canceled {
|
| - if (canceled) {
|
| - [_scene setPanVelocity:CGPointMake(0, 0)];
|
| - }
|
| - BOOL inMotion = [_scene tickPanVelocity];
|
| -
|
| - _singleTapRecognizer.enabled = !inMotion;
|
| - _longPressRecognizer.enabled = !inMotion;
|
| -}
|
| -
|
| -- (void)applySceneChange:(CGPoint)translation scaleBy:(float)ratio {
|
| - [_scene panAndZoom:translation scaleBy:ratio];
|
| - // Notify HOST that the mouse moved
|
| - [Utility moveMouse:_clientToHostProxy at:_scene.mousePosition];
|
| -}
|
| -
|
| -// Callback from NSNotificationCenter when the User changes orientation
|
| -- (void)orientationChanged:(NSNotification*)note {
|
| - [_scene
|
| - setContentSize:[Utility getOrientatedSize:self.view.bounds.size
|
| - shouldWidthBeLongestSide:[Utility isInLandscapeMode]]];
|
| - [self showToolbar:![self isToolbarHidden]];
|
| - [self updateLabels];
|
| -}
|
| -
|
| -// Animate |_toolbar| by moving it on or offscreen
|
| -- (void)showToolbar:(BOOL)visible {
|
| - CGRect frame = [_toolbar frame];
|
| -
|
| - _toolBarYPosition.constant = -frame.size.height;
|
| - int topOffset = kTopMargin;
|
| -
|
| - if (visible) {
|
| - topOffset += frame.size.height;
|
| - _toolBarYPosition.constant = kTopMargin;
|
| - }
|
| -
|
| - _hiddenToolbarYPosition.constant = topOffset;
|
| - [_scene setMarginsFromLeft:0 right:0 top:topOffset bottom:kBottomMargin];
|
| -
|
| - // hidden when |_toolbar| is |visible|
|
| - _hiddenToolbar.hidden = (visible == YES);
|
| -
|
| - [UIView animateWithDuration:0.5
|
| - animations:^{ [self.view layoutIfNeeded]; }
|
| - completion:^(BOOL finished) {// Nothing to do for now
|
| - }];
|
| -
|
| - // Center view if needed for any reason.
|
| - // Specificallly, if the top anchor is active.
|
| - [self applySceneChange:CGPointMake(0.0, 0.0) scaleBy:1.0];
|
| -}
|
| -@end
|
|
|