2013-02-14 17:59:12 +04:00
/ *
RDP Session View Controller
Copyright 2013 Thinstuff Technologies GmbH , Author : Martin Fleisz
This Source Code Form is subject to the terms of the Mozilla Public License , v . 2.0 .
If a copy of the MPL was not distributed with this file , You can obtain one at http : // mozilla . org / MPL / 2.0 /.
* /
# import < QuartzCore / QuartzCore . h >
# import "RDPSessionViewController.h"
# import "RDPKeyboard.h"
# import "Utils.h"
# import "Toast+UIView.h"
# import "ConnectionParams.h"
# import "CredentialsInputController.h"
# import "VerifyCertificateController.h"
2013-04-08 15:25:00 +04:00
# import "BlockAlertView.h"
2013-02-14 17:59:12 +04:00
# define TOOLBAR_HEIGHT 30
# define AUTOSCROLLDISTANCE 20
# define AUTOSCROLLTIMEOUT 0.05
@ interface RDPSessionViewController ( Private )
- ( void ) showSessionToolbar : ( BOOL ) show ;
- ( UIToolbar * ) keyboardToolbar ;
- ( void ) initGestureRecognizers ;
- ( void ) suspendSession ;
- ( NSDictionary * ) eventDescriptorForMouseEvent : ( int ) event position : ( CGPoint ) position ;
- ( void ) handleMouseMoveForPosition : ( CGPoint ) position ;
@ end
@ implementation RDPSessionViewController
# pragma mark class methods
- ( id ) initWithNibName : ( NSString * ) nibNameOrNil bundle : ( NSBundle * ) nibBundleOrNil session : ( RDPSession * ) session
{
self = [ super initWithNibName : nibNameOrNil bundle : nibBundleOrNil ] ;
if ( self )
{
_session = [ session retain ] ;
[ _session setDelegate : self ] ;
_session _initilized = NO ;
_mouse _move _events _skipped = 0 ;
_mouse _move _event _timer = nil ;
_advanced _keyboard _view = nil ;
_advanced _keyboard _visible = NO ;
_requesting _advanced _keyboard = NO ;
_session _toolbar _visible = NO ;
_toggle _mouse _button = NO ;
_autoscroll _with _touchpointer = [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "ui.auto_scroll_touchpointer" ] ;
_is _autoscrolling = NO ;
[ UIView setAnimationDelegate : self ] ;
[ UIView setAnimationDidStopSelector : @ selector ( animationStopped : finished : context : ) ] ;
}
return self ;
}
// Implement loadView to create a view hierarchy programmatically , without using a nib .
- ( void ) loadView
{
// load default view and set background color and resizing mask
[ super loadView ] ;
2013-03-18 19:31:14 +04:00
// init keyboard handling vars
2013-02-14 17:59:12 +04:00
_keyboard _visible = NO ;
// init keyboard toolbar
_keyboard _toolbar = [ [ self keyboardToolbar ] retain ] ;
[ _dummy _textfield setInputAccessoryView : _keyboard _toolbar ] ;
// init gesture recognizers
[ self initGestureRecognizers ] ;
// hide session toolbar
[ _session _toolbar setFrame : CGRectMake ( 0.0 , - TOOLBAR_HEIGHT , [ [ self view ] bounds ] . size . width , TOOLBAR_HEIGHT ) ] ;
}
// Implement viewDidLoad to do additional setup after loading the view , typically from a nib .
- ( void ) viewDidLoad
{
[ super viewDidLoad ] ;
}
- ( BOOL ) shouldAutorotateToInterfaceOrientation : ( UIInterfaceOrientation ) interfaceOrientation {
return YES ;
}
- ( void ) didRotateFromInterfaceOrientation : ( UIInterfaceOrientation ) fromInterfaceOrientation
{
if ( ! [ _touchpointer _view isHidden ] )
[ _touchpointer _view ensurePointerIsVisible ] ;
}
- ( void ) didReceiveMemoryWarning {
// Releases the view if it doesn ' t have a superview .
[ super didReceiveMemoryWarning ] ;
// Release any cached data , images , etc . that aren ' t in use .
}
- ( void ) viewDidUnload {
[ super viewDidUnload ] ;
// Release any retained subviews of the main view .
// e . g . self . myOutlet = nil ;
}
- ( void ) viewWillAppear : ( BOOL ) animated
{
[ super viewWillAppear : animated ] ;
// hide navigation bar and ( if enabled ) the status bar
if ( [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "ui.hide_status_bar" ] )
{
if ( animated = = YES )
[ [ UIApplication sharedApplication ] setStatusBarHidden : YES withAnimation : UIStatusBarAnimationSlide ] ;
else
[ [ UIApplication sharedApplication ] setStatusBarHidden : YES withAnimation : UIStatusBarAnimationNone ] ;
}
[ [ self navigationController ] setNavigationBarHidden : YES animated : animated ] ;
// if sesssion is suspended - notify that we got a new bitmap context
if ( [ _session isSuspended ] )
[ self sessionBitmapContextWillChange : _session ] ;
// init keyboard
[ [ RDPKeyboard getSharedRDPKeyboard ] initWithSession : _session delegate : self ] ;
}
- ( void ) viewDidAppear : ( BOOL ) animated
{
[ super viewDidAppear : animated ] ;
if ( ! _session _initilized )
{
if ( [ _session isSuspended ] )
{
[ _session resume ] ;
[ self sessionBitmapContextDidChange : _session ] ;
[ _session _view setNeedsDisplay ] ;
}
else
[ _session connect ] ;
_session _initilized = YES ;
}
}
- ( void ) viewWillDisappear : ( BOOL ) animated
{
[ super viewWillDisappear : animated ] ;
// show navigation and status bar again
if ( animated = = YES )
[ [ UIApplication sharedApplication ] setStatusBarHidden : NO withAnimation : UIStatusBarAnimationSlide ] ;
else
[ [ UIApplication sharedApplication ] setStatusBarHidden : NO withAnimation : UIStatusBarAnimationNone ] ;
[ [ self navigationController ] setNavigationBarHidden : NO animated : animated ] ;
// reset all modifier keys on rdp keyboard
[ [ RDPKeyboard getSharedRDPKeyboard ] reset ] ;
// hide toolbar and keyboard
[ self showSessionToolbar : NO ] ;
[ _dummy _textfield resignFirstResponder ] ;
}
- ( void ) dealloc {
// remove any observers
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
// the session lives on longer so set the delegate to nil
[ _session setDelegate : nil ] ;
[ _advanced _keyboard _view release ] ;
[ _keyboard _toolbar release ] ;
[ _session release ] ;
[ super dealloc ] ;
}
# pragma mark -
# pragma mark ScrollView delegate methods
- ( UIView * ) viewForZoomingInScrollView : ( UIScrollView * ) scrollView
{
return _session _view ;
}
- ( void ) scrollViewDidEndZooming : ( UIScrollView * ) scrollView withView : ( UIView * ) view atScale : ( float ) scale
{
NSLog ( @ "New zoom scale: %f" , scale ) ;
[ _session _view setNeedsDisplay ] ;
}
# pragma mark -
# pragma mark TextField delegate methods
- ( BOOL ) textFieldShouldBeginEditing : ( UITextField * ) textField
{
_keyboard _visible = YES ;
_advanced _keyboard _visible = NO ;
return YES ;
}
- ( BOOL ) textFieldShouldEndEditing : ( UITextField * ) textField
{
_keyboard _visible = NO ;
_advanced _keyboard _visible = NO ;
return YES ;
}
- ( BOOL ) textField : ( UITextField * ) textField shouldChangeCharactersInRange : ( NSRange ) range replacementString : ( NSString * ) string
{
if ( [ string length ] > 0 )
{
for ( int i = 0 ; i < [ string length ] ; i + + )
{
NSString * characterTyped = [ string substringWithRange : NSMakeRange ( i , 1 ) ] ;
unichar curChar = [ characterTyped characterAtIndex : 0 ] ;
// special handling for return / enter key
if ( curChar = = ' \ n ' )
[ [ RDPKeyboard getSharedRDPKeyboard ] sendEnterKeyStroke ] ;
else
[ [ RDPKeyboard getSharedRDPKeyboard ] sendUnicode : curChar ] ;
}
}
else
{
[ [ RDPKeyboard getSharedRDPKeyboard ] sendBackspaceKeyStroke ] ;
}
return NO ;
}
# pragma mark -
# pragma mark AdvancedKeyboardDelegate functions
- ( void ) advancedKeyPressedVKey : ( int ) key
{
[ [ RDPKeyboard getSharedRDPKeyboard ] sendVirtualKeyCode : key ] ;
}
- ( void ) advancedKeyPressedUnicode : ( int ) key
{
[ [ RDPKeyboard getSharedRDPKeyboard ] sendUnicode : key ] ;
}
# pragma mark - RDP keyboard handler
- ( void ) modifiersChangedForKeyboard : ( RDPKeyboard * ) keyboard
{
UIBarButtonItem * curItem ;
// shift button ( only on iPad )
int objectIdx = 0 ;
if ( IsPad ( ) )
{
objectIdx = 2 ;
curItem = ( UIBarButtonItem * ) [ [ _keyboard _toolbar items ] objectAtIndex : objectIdx ] ;
[ curItem setStyle : [ keyboard shiftPressed ] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered ] ;
}
// ctrl button
objectIdx + = 2 ;
curItem = ( UIBarButtonItem * ) [ [ _keyboard _toolbar items ] objectAtIndex : objectIdx ] ;
[ curItem setStyle : [ keyboard ctrlPressed ] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered ] ;
// win button
objectIdx + = 2 ;
curItem = ( UIBarButtonItem * ) [ [ _keyboard _toolbar items ] objectAtIndex : objectIdx ] ;
[ curItem setStyle : [ keyboard winPressed ] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered ] ;
// alt button
objectIdx + = 2 ;
curItem = ( UIBarButtonItem * ) [ [ _keyboard _toolbar items ] objectAtIndex : objectIdx ] ;
[ curItem setStyle : [ keyboard altPressed ] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered ] ;
}
# pragma mark -
# pragma mark RDPSessionDelegate functions
- ( void ) session : ( RDPSession * ) session didFailToConnect : ( int ) reason
{
// remove and release connecting view
[ _connecting _indicator _view stopAnimating ] ;
[ _connecting _view removeFromSuperview ] ;
[ _connecting _view autorelease ] ;
// return to bookmark list
[ [ self navigationController ] popViewControllerAnimated : YES ] ;
}
- ( void ) sessionWillConnect : ( RDPSession * ) session
{
// load connecting view
[ [ NSBundle mainBundle ] loadNibNamed : @ "RDPConnectingView" owner : self options : nil ] ;
// set strings
[ _lbl _connecting setText : NSLocalizedString ( @ "Connecting" , @ "Connecting progress view - label" ) ] ;
[ _cancel _connect _button setTitle : NSLocalizedString ( @ "Cancel" , @ "Cancel Button" ) forState : UIControlStateNormal ] ;
// center view and give it round corners
[ _connecting _view setCenter : [ [ self view ] center ] ] ;
[ [ _connecting _view layer ] setCornerRadius : 10 ] ;
// display connecting view and start indicator
[ [ self view ] addSubview : _connecting _view ] ;
[ _connecting _indicator _view startAnimating ] ;
}
- ( void ) sessionDidConnect : ( RDPSession * ) session
{
2013-03-18 19:31:14 +04:00
// register keyboard notification handlers
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( keyboardWillShow : ) name : UIKeyboardWillShowNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( keyboardDidShow : ) name : UIKeyboardDidShowNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( keyboardWillHide : ) name : UIKeyboardWillHideNotification object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( keyboardDidHide : ) name : UIKeyboardDidHideNotification object : nil ] ;
2013-02-14 17:59:12 +04:00
// remove and release connecting view
[ _connecting _indicator _view stopAnimating ] ;
[ _connecting _view removeFromSuperview ] ;
[ _connecting _view autorelease ] ;
// check if session settings changed . . .
// The 2 nd width check is to ignore changes in resolution settings due to the RDVH display bug ( refer to RDPSEssion . m for more details )
ConnectionParams * orig_params = [ session params ] ;
rdpSettings * sess_params = [ session getSessionParams ] ;
if ( ( [ orig_params intForKey : @ "width" ] ! = sess_params -> DesktopWidth && [ orig_params intForKey : @ "width" ] ! = ( sess_params -> DesktopWidth + 1 ) ) ||
[ orig_params intForKey : @ "height" ] ! = sess_params -> DesktopHeight || [ orig_params intForKey : @ "colors" ] ! = sess_params -> ColorDepth )
{
// display notification that the session params have been changed by the server
NSString * message = [ NSString stringWithFormat : NSLocalizedString ( @ "The server changed the screen settings to %dx%dx%d" , @ "Screen settings not supported message with width, height and colors parameter" ) , sess_params -> DesktopWidth , sess_params -> DesktopHeight , sess_params -> ColorDepth ] ;
[ [ self view ] makeToast : message duration : ToastDurationNormal position : @ "bottom" ] ;
}
}
- ( void ) sessionWillDisconnect : ( RDPSession * ) session
{
}
- ( void ) sessionDidDisconnect : ( RDPSession * ) session
{
// return to bookmark list
[ [ self navigationController ] popViewControllerAnimated : YES ] ;
}
- ( void ) sessionBitmapContextWillChange : ( RDPSession * ) session
{
// calc new view frame
rdpSettings * sess_params = [ session getSessionParams ] ;
CGRect view_rect = CGRectMake ( 0 , 0 , sess_params -> DesktopWidth , sess_params -> DesktopHeight ) ;
// reset zoom level and update content size
[ _session _scrollview setZoomScale : 1.0 ] ;
[ _session _scrollview setContentSize : view_rect . size ] ;
// set session view size
[ _session _view setFrame : view_rect ] ;
// show / hide toolbar
[ _session setToolbarVisible : ! [ [ NSUserDefaults standardUserDefaults ] boolForKey : @ "ui.hide_tool_bar" ] ] ;
[ self showSessionToolbar : [ _session toolbarVisible ] ] ;
}
- ( void ) sessionBitmapContextDidChange : ( RDPSession * ) session
{
// associate view with session
2013-03-05 20:08:53 +04:00
[ _session _view setSession : session ] ;
// issue an update ( this might be needed in case we had a resize for instance )
[ _session _view setNeedsDisplay ] ;
2013-02-14 17:59:12 +04:00
}
- ( void ) session : ( RDPSession * ) session needsRedrawInRect : ( CGRect ) rect
{
2013-03-18 19:31:14 +04:00
[ _session _view setNeedsDisplayInRect : rect ] ;
2013-02-14 17:59:12 +04:00
}
- ( void ) session : ( RDPSession * ) session requestsAuthenticationWithParams : ( NSMutableDictionary * ) params
{
CredentialsInputController * view_controller = [ [ [ CredentialsInputController alloc ] initWithNibName : @ "CredentialsInputView" bundle : nil session : _session params : params ] autorelease ] ;
[ self presentModalViewController : view_controller animated : YES ] ;
}
- ( void ) session : ( RDPSession * ) session verifyCertificateWithParams : ( NSMutableDictionary * ) params
{
VerifyCertificateController * view_controller = [ [ [ VerifyCertificateController alloc ] initWithNibName : @ "VerifyCertificateView" bundle : nil session : _session params : params ] autorelease ] ;
[ self presentModalViewController : view_controller animated : YES ] ;
}
- ( CGSize ) sizeForFitScreenForSession : ( RDPSession * ) session
{
if ( IsPad ( ) )
return [ self view ] . bounds . size ;
else
{
// on phones make a resolution that has a 16 : 10 ratio with the phone ' s height
CGSize size = [ self view ] . bounds . size ;
CGFloat maxSize = ( size . width > size . height ) ? size . width : size . height ;
return CGSizeMake ( maxSize * 1.6 f , maxSize ) ;
}
}
- ( void ) showGoProScreen : ( RDPSession * ) session
{
2013-04-08 15:25:00 +04:00
BlockAlertView * alertView = [ BlockAlertView alertWithTitle : NSLocalizedString ( @ "Unlicensed Client" , @ "Pro version dialog title" ) message : NSLocalizedString ( @ "You are connected to Thinstuff Remote Desktop Host (RDH). Do you want to purchase an access license for this client which allows you to connect to any computer running Thinstuff RDH?" , @ "Pro version dialog message" ) ] ;
[ alertView setCancelButtonWithTitle : NSLocalizedString ( @ "No" , @ "No Button title" ) block : nil ] ;
[ alertView addButtonWithTitle : NSLocalizedString ( @ "Yes" , @ "Yes button title" ) block : ^ {
} ] ;
2013-02-14 17:59:12 +04:00
[ alertView show ] ;
}
# pragma mark - Keyboard Toolbar Handlers
- ( void ) showAdvancedKeyboardAnimated
{
// calc initial and final rect of the advanced keyboard view
CGRect rect = [ [ _keyboard _toolbar superview ] bounds ] ;
rect . origin . y = [ _keyboard _toolbar bounds ] . size . height ;
rect . size . height - = rect . origin . y ;
// create new view ( hidden ) and add to host - view of keyboard toolbar
_advanced _keyboard _view = [ [ AdvancedKeyboardView alloc ] initWithFrame : CGRectMake ( rect . origin . x ,
[ [ _keyboard _toolbar superview ] bounds ] . size . height ,
rect . size . width , rect . size . height ) delegate : self ] ;
[ [ _keyboard _toolbar superview ] addSubview : _advanced _keyboard _view ] ;
// we set autoresize to YES for the keyboard toolbar ' s superview so that our adv . keyboard view gets properly resized
[ [ _keyboard _toolbar superview ] setAutoresizesSubviews : YES ] ;
// show view with animation
[ UIView beginAnimations : nil context : NULL ] ;
[ _advanced _keyboard _view setFrame : rect ] ;
[ UIView commitAnimations ] ;
}
- ( IBAction ) toggleKeyboardWhenOtherVisible : ( id ) sender
{
if ( _advanced _keyboard _visible = = NO )
{
[ self showAdvancedKeyboardAnimated ] ;
}
else
{
// hide existing view
[ UIView beginAnimations : @ "hide_advanced_keyboard_view" context : NULL ] ;
CGRect rect = [ _advanced _keyboard _view frame ] ;
rect . origin . y = [ [ _keyboard _toolbar superview ] bounds ] . size . height ;
[ _advanced _keyboard _view setFrame : rect ] ;
[ UIView commitAnimations ] ;
// the view is released in the animationDidStop selector registered in init
}
// toggle flag
_advanced _keyboard _visible = ! _advanced _keyboard _visible ;
}
- ( IBAction ) toggleWinKey : ( id ) sender
{
[ [ RDPKeyboard getSharedRDPKeyboard ] toggleWinKey ] ;
}
- ( IBAction ) toggleShiftKey : ( id ) sender
{
[ [ RDPKeyboard getSharedRDPKeyboard ] toggleShiftKey ] ;
}
- ( IBAction ) toggleCtrlKey : ( id ) sender
{
[ [ RDPKeyboard getSharedRDPKeyboard ] toggleCtrlKey ] ;
}
- ( IBAction ) toggleAltKey : ( id ) sender
{
[ [ RDPKeyboard getSharedRDPKeyboard ] toggleAltKey ] ;
}
- ( IBAction ) pressEscKey : ( id ) sender
{
[ [ RDPKeyboard getSharedRDPKeyboard ] sendEscapeKeyStroke ] ;
}
# pragma mark -
# pragma mark event handlers
- ( void ) animationStopped : ( NSString * ) animationID finished : ( NSNumber * ) finished context : ( void * ) context
{
if ( [ animationID isEqualToString : @ "hide_advanced_keyboard_view" ] )
{
// cleanup advanced keyboard view
[ _advanced _keyboard _view removeFromSuperview ] ;
[ _advanced _keyboard _view autorelease ] ;
_advanced _keyboard _view = nil ;
}
}
- ( IBAction ) switchSession : ( id ) sender
{
[ self suspendSession ] ;
}
- ( IBAction ) toggleKeyboard : ( id ) sender
{
if ( ! _keyboard _visible )
[ _dummy _textfield becomeFirstResponder ] ;
else
[ _dummy _textfield resignFirstResponder ] ;
}
- ( IBAction ) toggleExtKeyboard : ( id ) sender
{
// if the sys kb is shown but not the advanced kb then toggle the advanced kb
if ( _keyboard _visible && ! _advanced _keyboard _visible )
[ self toggleKeyboardWhenOtherVisible : nil ] ;
else
{
// if not visible request the advanced keyboard view
if ( _advanced _keyboard _visible = = NO )
_requesting _advanced _keyboard = YES ;
[ self toggleKeyboard : nil ] ;
}
}
- ( IBAction ) toggleTouchPointer : ( id ) sender
{
BOOL toggle_visibilty = ! [ _touchpointer _view isHidden ] ;
[ _touchpointer _view setHidden : toggle_visibilty ] ;
if ( toggle_visibilty )
[ _session _scrollview setContentInset : UIEdgeInsetsZero ] ;
else
[ _session _scrollview setContentInset : [ _touchpointer _view getEdgeInsets ] ] ;
}
- ( IBAction ) disconnectSession : ( id ) sender
{
[ _session disconnect ] ;
}
- ( IBAction ) cancelButtonPressed : ( id ) sender
{
[ _session disconnect ] ;
}
# pragma mark In - App purchase transaction notification handlers
- ( void ) onTransactionSuccess : ( NSNotification * ) notification
{
2013-04-08 15:25:00 +04:00
BlockAlertView * alertView = [ BlockAlertView alertWithTitle : NSLocalizedString ( @ "Transaction Succeeded" , @ "Pro version bought dialog title" )
message : NSLocalizedString ( @ "Thanks for buying Thinstuff RDC Pro. In order for the purchase to take effect please reconnect your current session." , @ "Pro version bought dialog message" ) ] ;
[ alertView setCancelButtonWithTitle : NSLocalizedString ( @ "OK" , @ "OK Button title" ) block : nil ] ;
2013-02-14 17:59:12 +04:00
[ alertView show ] ;
}
- ( void ) onTransactionFailed : ( NSNotification * ) notification
{
2013-04-08 15:25:00 +04:00
BlockAlertView * alertView = [ BlockAlertView alertWithTitle : NSLocalizedString ( @ "Transaction Failed" , @ "Pro version buy failed dialog title" )
message : NSLocalizedString ( @ "The transaction did not complete successfully!" , @ "Pro version buy failed dialog message" ) ] ;
[ alertView setCancelButtonWithTitle : NSLocalizedString ( @ "OK" , @ "OK Button title" ) block : nil ] ;
[ alertView show ] ;
2013-02-14 17:59:12 +04:00
}
# pragma mark -
# pragma mark iOS Keyboard Notification Handlers
- ( void ) keyboardWillShow : ( NSNotification * ) notification
{
CGRect keyboardEndFrame = [ [ [ notification userInfo ] objectForKey : UIKeyboardFrameEndUserInfoKey ] CGRectValue ] ;
[ UIView beginAnimations : nil context : NULL ] ;
[ UIView setAnimationCurve : [ [ [ notification userInfo ] objectForKey : UIKeyboardAnimationCurveUserInfoKey ] intValue ] ] ;
[ UIView setAnimationDuration : [ [ [ notification userInfo ] objectForKey : UIKeyboardAnimationDurationUserInfoKey ] doubleValue ] ] ;
CGRect frame = [ _session _scrollview frame ] ;
frame . size . height - = [ [ self view ] convertRect : keyboardEndFrame toView : nil ] . size . height ;
[ _session _scrollview setFrame : frame ] ;
[ _touchpointer _view setFrame : frame ] ;
[ UIView commitAnimations ] ;
[ _touchpointer _view ensurePointerIsVisible ] ;
}
- ( void ) keyboardDidShow : ( NSNotification * ) notification
{
if ( _requesting _advanced _keyboard )
{
[ self showAdvancedKeyboardAnimated ] ;
_advanced _keyboard _visible = YES ;
_requesting _advanced _keyboard = NO ;
}
}
- ( void ) keyboardWillHide : ( NSNotification * ) notification
{
CGRect keyboardEndFrame = [ [ [ notification userInfo ] objectForKey : UIKeyboardFrameEndUserInfoKey ] CGRectValue ] ;
[ UIView beginAnimations : nil context : NULL ] ;
[ UIView setAnimationCurve : [ [ [ notification userInfo ] objectForKey : UIKeyboardAnimationCurveUserInfoKey ] intValue ] ] ;
[ UIView setAnimationDuration : [ [ [ notification userInfo ] objectForKey : UIKeyboardAnimationDurationUserInfoKey ] doubleValue ] ] ;
CGRect frame = [ _session _scrollview frame ] ;
frame . size . height + = [ [ self view ] convertRect : keyboardEndFrame toView : nil ] . size . height ;
[ _session _scrollview setFrame : frame ] ;
[ _touchpointer _view setFrame : frame ] ;
[ UIView commitAnimations ] ;
}
- ( void ) keyboardDidHide : ( NSNotification * ) notification
{
// release adanced keyboard view
if ( _advanced _keyboard _visible = = YES )
{
_advanced _keyboard _visible = NO ;
[ _advanced _keyboard _view removeFromSuperview ] ;
[ _advanced _keyboard _view autorelease ] ;
_advanced _keyboard _view = nil ;
}
}
# pragma mark -
# pragma mark Gesture handlers
- ( void ) handleSingleTap : ( UITapGestureRecognizer * ) gesture
{
CGPoint pos = [ gesture locationInView : _session _view ] ;
if ( _toggle _mouse _button )
{
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetRightMouseButtonClickEvent ( YES ) position : pos ] ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetRightMouseButtonClickEvent ( NO ) position : pos ] ] ;
}
else
{
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( YES ) position : pos ] ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( NO ) position : pos ] ] ;
}
_toggle _mouse _button = NO ;
}
- ( void ) handleDoubleTap : ( UITapGestureRecognizer * ) gesture
{
CGPoint pos = [ gesture locationInView : _session _view ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( YES ) position : pos ] ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( NO ) position : pos ] ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( YES ) position : pos ] ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( NO ) position : pos ] ] ;
_toggle _mouse _button = NO ;
}
- ( void ) handleLongPress : ( UILongPressGestureRecognizer * ) gesture
{
CGPoint pos = [ gesture locationInView : _session _view ] ;
if ( [ gesture state ] = = UIGestureRecognizerStateBegan )
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( YES ) position : pos ] ] ;
else if ( [ gesture state ] = = UIGestureRecognizerStateChanged )
[ self handleMouseMoveForPosition : pos ] ;
else if ( [ gesture state ] = = UIGestureRecognizerStateEnded )
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( NO ) position : pos ] ] ;
}
- ( void ) handleDoubleLongPress : ( UILongPressGestureRecognizer * ) gesture
{
// this point is mapped against the scroll view because we want to have relative movement to the screen / scrollview
CGPoint pos = [ gesture locationInView : _session _scrollview ] ;
if ( [ gesture state ] = = UIGestureRecognizerStateBegan )
_prev _long _press _position = pos ;
else if ( [ gesture state ] = = UIGestureRecognizerStateChanged )
{
int delta = _prev _long _press _position . y - pos . y ;
if ( delta > GetScrollGestureDelta ( ) )
{
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetMouseWheelEvent ( YES ) position : pos ] ] ;
_prev _long _press _position = pos ;
}
else if ( delta < - GetScrollGestureDelta ( ) )
{
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetMouseWheelEvent ( NO ) position : pos ] ] ;
_prev _long _press _position = pos ;
}
}
}
- ( void ) handleSingle2FingersTap : ( UITapGestureRecognizer * ) gesture
{
_toggle _mouse _button = ! _toggle _mouse _button ;
}
- ( void ) handleSingle3FingersTap : ( UITapGestureRecognizer * ) gesture
{
[ _session setToolbarVisible : ! [ _session toolbarVisible ] ] ;
[ self showSessionToolbar : [ _session toolbarVisible ] ] ;
}
# pragma mark -
# pragma mark Touch Pointer delegates
// callback if touch pointer should be closed
- ( void ) touchPointerClose
{
[ self toggleTouchPointer : nil ] ;
}
// callback for a left click action
- ( void ) touchPointerLeftClick : ( CGPoint ) pos down : ( BOOL ) down
{
CGPoint session_view _pos = [ _touchpointer _view convertPoint : pos toView : _session _view ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetLeftMouseButtonClickEvent ( down ) position : session_view _pos ] ] ;
}
// callback for a right click action
- ( void ) touchPointerRightClick : ( CGPoint ) pos down : ( BOOL ) down
{
CGPoint session_view _pos = [ _touchpointer _view convertPoint : pos toView : _session _view ] ;
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetRightMouseButtonClickEvent ( down ) position : session_view _pos ] ] ;
}
- ( void ) doAutoScrolling
{
int scrollX = 0 ;
int scrollY = 0 ;
CGPoint curPointerPos = [ _touchpointer _view getPointerPosition ] ;
CGRect viewBounds = [ _touchpointer _view bounds ] ;
CGRect scrollBounds = [ _session _view bounds ] ;
// add content insets to scroll bounds
scrollBounds . size . width + = [ _session _scrollview contentInset ] . right ;
scrollBounds . size . height + = [ _session _scrollview contentInset ] . bottom ;
// add zoom factor
scrollBounds . size . width * = [ _session _scrollview zoomScale ] ;
scrollBounds . size . height * = [ _session _scrollview zoomScale ] ;
if ( curPointerPos . x > ( viewBounds . size . width - [ _touchpointer _view getPointerWidth ] ) )
scrollX = AUTOSCROLLDISTANCE ;
else if ( curPointerPos . x < 0 )
scrollX = - AUTOSCROLLDISTANCE ;
if ( curPointerPos . y > ( viewBounds . size . height - [ _touchpointer _view getPointerHeight ] ) )
scrollY = AUTOSCROLLDISTANCE ;
else if ( curPointerPos . y < ( _session _toolbar _visible ? TOOLBAR_HEIGHT : 0 ) )
scrollY = - AUTOSCROLLDISTANCE ;
CGPoint newOffset = [ _session _scrollview contentOffset ] ;
newOffset . x + = scrollX ;
newOffset . y + = scrollY ;
// if offset is going off screen - stop scrolling in that direction
if ( newOffset . x < 0 )
{
scrollX = 0 ;
newOffset . x = 0 ;
}
else if ( newOffset . x > ( scrollBounds . size . width - viewBounds . size . width ) )
{
scrollX = 0 ;
newOffset . x = MAX ( scrollBounds . size . width - viewBounds . size . width , 0 ) ;
}
if ( newOffset . y < 0 )
{
scrollY = 0 ;
newOffset . y = 0 ;
}
else if ( newOffset . y > ( scrollBounds . size . height - viewBounds . size . height ) )
{
scrollY = 0 ;
newOffset . y = MAX ( scrollBounds . size . height - viewBounds . size . height , 0 ) ;
}
// perform scrolling
[ _session _scrollview setContentOffset : newOffset ] ;
// continue scrolling ?
if ( scrollX ! = 0 || scrollY ! = 0 )
[ self performSelector : @ selector ( doAutoScrolling ) withObject : nil afterDelay : AUTOSCROLLTIMEOUT ] ;
else
_is _autoscrolling = NO ;
}
// callback for a right click action
- ( void ) touchPointerMove : ( CGPoint ) pos
{
CGPoint session_view _pos = [ _touchpointer _view convertPoint : pos toView : _session _view ] ;
[ self handleMouseMoveForPosition : session_view _pos ] ;
if ( _autoscroll _with _touchpointer && ! _is _autoscrolling )
{
_is _autoscrolling = YES ;
[ self performSelector : @ selector ( doAutoScrolling ) withObject : nil afterDelay : AUTOSCROLLTIMEOUT ] ;
}
}
// callback if scrolling is performed
- ( void ) touchPointerScrollDown : ( BOOL ) down
{
[ _session sendInputEvent : [ self eventDescriptorForMouseEvent : GetMouseWheelEvent ( down ) position : CGPointZero ] ] ;
}
// callback for toggling the standard keyboard
- ( void ) touchPointerToggleKeyboard
{
if ( _advanced _keyboard _visible )
[ self toggleKeyboardWhenOtherVisible : nil ] ;
else
[ self toggleKeyboard : nil ] ;
}
// callback for toggling the extended keyboard
- ( void ) touchPointerToggleExtendedKeyboard
{
[ self toggleExtKeyboard : nil ] ;
}
// callback for reset view
- ( void ) touchPointerResetSessionView
{
[ _session _scrollview setZoomScale : 1.0 animated : YES ] ;
}
@ end
@ implementation RDPSessionViewController ( Private )
# pragma mark -
# pragma mark Helper functions
- ( void ) showSessionToolbar : ( BOOL ) show
{
// already shown or hidden ?
if ( _session _toolbar _visible = = show )
return ;
if ( show )
{
[ UIView beginAnimations : @ "showToolbar" context : nil ] ;
[ UIView setAnimationDuration : .4 ] ;
[ UIView setAnimationCurve : UIViewAnimationCurveLinear ] ;
[ _session _toolbar setFrame : CGRectMake ( 0.0 , 0.0 , [ [ self view ] bounds ] . size . width , TOOLBAR_HEIGHT ) ] ;
[ UIView commitAnimations ] ;
_session _toolbar _visible = YES ;
}
else
{
[ UIView beginAnimations : @ "hideToolbar" context : nil ] ;
[ UIView setAnimationDuration : .4 ] ;
[ UIView setAnimationCurve : UIViewAnimationCurveLinear ] ;
[ _session _toolbar setFrame : CGRectMake ( 0.0 , - TOOLBAR_HEIGHT , [ [ self view ] bounds ] . size . width , TOOLBAR_HEIGHT ) ] ;
[ UIView commitAnimations ] ;
_session _toolbar _visible = NO ;
}
}
- ( UIToolbar * ) keyboardToolbar
{
UIToolbar * keyboard_toolbar = [ [ [ UIToolbar alloc ] initWithFrame : CGRectNull ] autorelease ] ;
[ keyboard_toolbar setBarStyle : UIBarStyleBlackOpaque ] ;
UIBarButtonItem * esc_btn = [ [ [ UIBarButtonItem alloc ] initWithTitle : @ "Esc" style : UIBarButtonItemStyleBordered target : self action : @ selector ( pressEscKey : ) ] autorelease ] ;
UIImage * win_icon = [ UIImage imageWithContentsOfFile : [ [ NSBundle mainBundle ] pathForResource : @ "toolbar_icon_win" ofType : @ "png" ] ] ;
UIBarButtonItem * win_btn = [ [ [ UIBarButtonItem alloc ] initWithImage : win_icon style : UIBarButtonItemStyleBordered target : self action : @ selector ( toggleWinKey : ) ] autorelease ] ;
UIBarButtonItem * ctrl_btn = [ [ [ UIBarButtonItem alloc ] initWithTitle : @ "Ctrl" style : UIBarButtonItemStyleBordered target : self action : @ selector ( toggleCtrlKey : ) ] autorelease ] ;
UIBarButtonItem * alt_btn = [ [ [ UIBarButtonItem alloc ] initWithTitle : @ "Alt" style : UIBarButtonItemStyleBordered target : self action : @ selector ( toggleAltKey : ) ] autorelease ] ;
UIBarButtonItem * ext_btn = [ [ [ UIBarButtonItem alloc ] initWithTitle : @ "Ext" style : UIBarButtonItemStyleBordered target : self action : @ selector ( toggleKeyboardWhenOtherVisible : ) ] autorelease ] ;
UIBarButtonItem * done_btn = [ [ [ UIBarButtonItem alloc ] initWithBarButtonSystemItem : UIBarButtonSystemItemDone target : self action : @ selector ( toggleKeyboard : ) ] autorelease ] ;
UIBarButtonItem * flex_spacer = [ [ [ UIBarButtonItem alloc ] initWithBarButtonSystemItem : UIBarButtonSystemItemFlexibleSpace target : nil action : nil ] autorelease ] ;
// iPad gets a shift button , iphone doesn ' t ( there ' s just not enough space . . . )
NSArray * items ;
if ( IsPad ( ) )
{
UIBarButtonItem * shift_btn = [ [ [ UIBarButtonItem alloc ] initWithTitle : @ "Shift" style : UIBarButtonItemStyleBordered target : self action : @ selector ( toggleShiftKey : ) ] autorelease ] ;
items = [ NSArray arrayWithObjects : esc_btn , flex_spacer ,
shift_btn , flex_spacer ,
ctrl_btn , flex_spacer ,
win_btn , flex_spacer ,
alt_btn , flex_spacer ,
ext_btn , flex_spacer , done_btn , nil ] ;
}
else
{
items = [ NSArray arrayWithObjects : esc_btn , flex_spacer , ctrl_btn , flex_spacer , win_btn , flex_spacer , alt_btn , flex_spacer , ext_btn , flex_spacer , done_btn , nil ] ;
}
[ keyboard_toolbar setItems : items ] ;
[ keyboard_toolbar sizeToFit ] ;
return keyboard_toolbar ;
}
- ( void ) initGestureRecognizers
{
// single and double tap recognizer
UITapGestureRecognizer * doubleTapRecognizer = [ [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleDoubleTap : ) ] autorelease ] ;
[ doubleTapRecognizer setNumberOfTouchesRequired : 1 ] ;
[ doubleTapRecognizer setNumberOfTapsRequired : 2 ] ;
UITapGestureRecognizer * singleTapRecognizer = [ [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSingleTap : ) ] autorelease ] ;
[ singleTapRecognizer requireGestureRecognizerToFail : doubleTapRecognizer ] ;
[ singleTapRecognizer setNumberOfTouchesRequired : 1 ] ;
[ singleTapRecognizer setNumberOfTapsRequired : 1 ] ;
// 2 fingers - tap recognizer
UITapGestureRecognizer * single2FingersTapRecognizer = [ [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSingle2FingersTap : ) ] autorelease ] ;
[ single2FingersTapRecognizer setNumberOfTouchesRequired : 2 ] ;
[ single2FingersTapRecognizer setNumberOfTapsRequired : 1 ] ;
// long press gesture recognizer
UILongPressGestureRecognizer * longPressRecognizer = [ [ [ UILongPressGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleLongPress : ) ] autorelease ] ;
[ longPressRecognizer setMinimumPressDuration : 0.5 ] ;
// double long press gesture recognizer
UILongPressGestureRecognizer * doubleLongPressRecognizer = [ [ [ UILongPressGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleDoubleLongPress : ) ] autorelease ] ;
[ doubleLongPressRecognizer setNumberOfTouchesRequired : 2 ] ;
[ doubleLongPressRecognizer setMinimumPressDuration : 0.5 ] ;
// 3 finger , single tap gesture for showing / hiding the toolbar
UITapGestureRecognizer * single3FingersTapRecognizer = [ [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( handleSingle3FingersTap : ) ] autorelease ] ;
[ single3FingersTapRecognizer setNumberOfTapsRequired : 1 ] ;
[ single3FingersTapRecognizer setNumberOfTouchesRequired : 3 ] ;
// add gestures to scroll view
[ _session _scrollview addGestureRecognizer : singleTapRecognizer ] ;
[ _session _scrollview addGestureRecognizer : doubleTapRecognizer ] ;
[ _session _scrollview addGestureRecognizer : single2FingersTapRecognizer ] ;
[ _session _scrollview addGestureRecognizer : longPressRecognizer ] ;
[ _session _scrollview addGestureRecognizer : doubleLongPressRecognizer ] ;
[ _session _scrollview addGestureRecognizer : single3FingersTapRecognizer ] ;
}
- ( void ) suspendSession
{
// suspend session and pop navigation controller
[ _session suspend ] ;
// pop current view controller
[ [ self navigationController ] popViewControllerAnimated : YES ] ;
}
- ( NSDictionary * ) eventDescriptorForMouseEvent : ( int ) event position : ( CGPoint ) position
{
return [ NSDictionary dictionaryWithObjectsAndKeys :
@ "mouse" , @ "type" ,
[ NSNumber numberWithUnsignedShort : event ] , @ "flags" ,
[ NSNumber numberWithUnsignedShort : lrintf ( position . x ) ] , @ "coord_x" ,
[ NSNumber numberWithUnsignedShort : lrintf ( position . y ) ] , @ "coord_y" ,
nil ] ;
}
- ( void ) sendDelayedMouseEventWithTimer : ( NSTimer * ) timer
{
_mouse _move _event _timer = nil ;
NSDictionary * event = [ timer userInfo ] ;
[ _session sendInputEvent : event ] ;
[ timer autorelease ] ;
}
- ( void ) handleMouseMoveForPosition : ( CGPoint ) position
{
NSDictionary * event = [ self eventDescriptorForMouseEvent : PTR_FLAGS _MOVE position : position ] ;
// cancel pending mouse move events
[ _mouse _move _event _timer invalidate ] ;
_mouse _move _events _skipped + + ;
if ( _mouse _move _events _skipped >= 5 )
{
[ _session sendInputEvent : event ] ;
_mouse _move _events _skipped = 0 ;
}
else
{
[ _mouse _move _event _timer autorelease ] ;
_mouse _move _event _timer = [ [ NSTimer scheduledTimerWithTimeInterval : 0.05 target : self selector : @ selector ( sendDelayedMouseEventWithTimer : ) userInfo : event repeats : NO ] retain ] ;
}
}
@ end