2013-02-14 17:59:12 +04:00
/ *
RDP Session View Controller
2013-12-04 14:37:57 +04:00
Copyright 2013 Thincast Technologies GmbH , Author : Martin Fleisz
2013-02-14 17:59:12 +04:00
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 ;
2013-04-25 14:31:11 +04:00
_requesting _advanced _keyboard = NO ;
2013-12-12 01:13:01 +04:00
_keyboard _last _height = 0 ;
2013-02-14 17:59:12 +04:00
_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 + + )
{
2013-04-25 14:31:11 +04:00
unichar curChar = [ string characterAtIndex : i ] ;
2013-02-14 17:59:12 +04:00
// 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 ) ;
}
}
# 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 -
# pragma mark iOS Keyboard Notification Handlers
2013-12-12 01:13:01 +04:00
// the keyboard is given in a portrait frame of reference
- ( BOOL ) isLandscape {
UIInterfaceOrientation ori = [ [ UIApplication sharedApplication ] statusBarOrientation ] ;
return ( ori = = UIInterfaceOrientationLandscapeLeft || ori = = UIInterfaceOrientationLandscapeRight ) ;
}
2013-04-25 14:31:11 +04:00
2013-12-12 01:13:01 +04:00
- ( void ) shiftKeyboard : ( NSNotification * ) notification {
CGRect keyboardEndFrame = [ [ [ notification userInfo ] objectForKey : UIKeyboardFrameEndUserInfoKey ] CGRectValue ] ;
CGFloat previousHeight = _keyboard _last _height ;
if ( [ self isLandscape ] ) {
// landscape has the keyboard based on x , so x can go negative
_keyboard _last _height = keyboardEndFrame . size . width + keyboardEndFrame . origin . x ;
} else {
// portrait has the keyboard based on the difference of the height and the frames y .
CGFloat height = [ [ UIScreen mainScreen ] bounds ] . size . height ;
_keyboard _last _height = height - keyboardEndFrame . origin . y ;
}
CGFloat shiftHeight = _keyboard _last _height - previousHeight ;
[ UIView beginAnimations : nil context : NULL ] ;
2013-02-14 17:59:12 +04:00
[ UIView setAnimationCurve : [ [ [ notification userInfo ] objectForKey : UIKeyboardAnimationCurveUserInfoKey ] intValue ] ] ;
[ UIView setAnimationDuration : [ [ [ notification userInfo ] objectForKey : UIKeyboardAnimationDurationUserInfoKey ] doubleValue ] ] ;
CGRect frame = [ _session _scrollview frame ] ;
2013-12-12 01:13:01 +04:00
frame . size . height - = shiftHeight ;
2013-02-14 17:59:12 +04:00
[ _session _scrollview setFrame : frame ] ;
2013-12-12 01:13:01 +04:00
[ _touchpointer _view setFrame : frame ] ;
2013-02-14 17:59:12 +04:00
[ UIView commitAnimations ] ;
2013-12-12 01:13:01 +04:00
}
2013-02-14 17:59:12 +04:00
2013-12-12 01:13:01 +04:00
- ( void ) keyboardWillShow : ( NSNotification * ) notification
{
[ self shiftKeyboard : notification ] ;
2013-02-14 17:59:12 +04:00
[ _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
{
2013-12-12 01:13:01 +04:00
[ self shiftKeyboard : notification ] ;
2013-02-14 17:59:12 +04:00
}
- ( 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