11#import <QuartzCore/QuartzCore.h>
12#import <GameController/GameController.h>
13#import <objc/runtime.h>
14#import "RDPSessionViewController.h"
15#import "RDPSessionToolbar.h"
16#import "RDPKeyboard.h"
18#import "Toast+UIView.h"
19#import "ConnectionParams.h"
20#import "CredentialsInputController.h"
21#import "VerifyCertificateController.h"
22#import "BlockAlertView.h"
24#define TOOLBAR_HEIGHT 44
26@interface RDPSessionViewController (Private)
27- (void)showSessionToolbar:(BOOL)show;
28- (UIToolbar *)keyboardToolbar;
29- (void)initGestureRecognizers;
30- (void)suspendSession;
31- (void)fitSessionViewToViewport;
32- (void)centerSessionViewInViewport;
33- (CGPoint)remotePositionForSessionViewPosition:(CGPoint)position;
34- (CGPoint)sessionViewPositionForRemotePosition:(CGPoint)position;
35- (CGPoint)clampedSessionViewCursorPosition:(CGPoint)position;
36- (NSDictionary *)eventDescriptorForMouseEvent:(
int)event position:(CGPoint)position;
37- (void)handleMouseMoveForPosition:(CGPoint)position;
38- (CGPoint)currentCursorViewPosition;
39- (void)moveCursorByViewportDelta:(CGPoint)delta;
40- (void)moveCursorToSessionViewPosition:(CGPoint)position;
41- (void)sendMouseButtonEvent:(
int)event;
46#pragma mark class methods
48- (id)initWithNibName:(NSString *)nibNameOrNil
49 bundle:(NSBundle *)nibBundleOrNil
52 self = [
super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
55 _session = [session retain];
56 [_session setDelegate:self];
57 _session_initilized = NO;
59 _advanced_keyboard_view = nil;
60 _advanced_keyboard_visible = NO;
61 _requesting_advanced_keyboard = NO;
62 _last_session_viewport_size = CGSizeZero;
64 _session_toolbar_visible = NO;
66 _cursor_view_position = CGPointZero;
67 _last_mouse_pan_location = CGPointZero;
68 _has_cursor_view_position = NO;
69 _has_user_moved_cursor = NO;
70 _mouse_pan_active = NO;
71 _long_press_active = NO;
72 _mouse_drag_active = NO;
73 _pointer_is_indirect = NO;
75 [UIView setAnimationDelegate:self];
76 [UIView setAnimationDidStopSelector:@selector(animationStopped:finished:context:)];
93 _keyboard_visible = NO;
96 _keyboard_toolbar = [[
self keyboardToolbar] retain];
97 [_dummy_textfield setInputAccessoryView:_keyboard_toolbar];
100 [
self initGestureRecognizers];
104 setFrame:CGRectMake(0.0, -TOOLBAR_HEIGHT, [[
self view] bounds].size.width, TOOLBAR_HEIGHT)];
112 [_session_scrollview setContentInsetAdjustmentBehavior:UIScrollViewContentInsetAdjustmentNever];
113 [_session_scrollview setContentInset:UIEdgeInsetsZero];
114 [_session_scrollview setScrollIndicatorInsets:UIEdgeInsetsZero];
115 [_session_scrollview setShowsHorizontalScrollIndicator:NO];
116 [_session_scrollview setShowsVerticalScrollIndicator:NO];
117 [_session_scrollview setAlwaysBounceHorizontal:NO];
118 [_session_scrollview setAlwaysBounceVertical:NO];
119 [_session_scrollview setBounces:NO];
122- (void)viewDidLayoutSubviews
124 [
super viewDidLayoutSubviews];
126 CGRect viewportFrame = [[
self view] bounds];
127 [_session_scrollview setFrame:viewportFrame];
129 CGSize viewportSize = [_session_scrollview bounds].size;
130 if (!CGSizeEqualToSize(viewportSize, _last_session_viewport_size))
131 [
self fitSessionViewToViewport];
133 [
self centerSessionViewInViewport];
136- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
141- (BOOL)prefersStatusBarHidden
146- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation
148 return UIStatusBarAnimationSlide;
151- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
153 (void)fromInterfaceOrientation;
154 [
self centerSessionViewInViewport];
157- (void)didReceiveMemoryWarning
160 [
super didReceiveMemoryWarning];
167 [
super viewDidUnload];
172- (void)viewWillAppear:(BOOL)animated
174 [
super viewWillAppear:animated];
177 [
self setNeedsStatusBarAppearanceUpdate];
178 [[
self navigationController] setNeedsStatusBarAppearanceUpdate];
179 [[
self navigationController] setNavigationBarHidden:YES animated:animated];
180 if (@available(iOS 18.0, *))
181 [[
self tabBarController] setTabBarHidden:YES animated:animated];
183 [[[
self tabBarController] tabBar] setHidden:YES];
186 if ([_session isSuspended])
187 [
self sessionBitmapContextWillChange:_session];
190 [[
RDPKeyboard getSharedRDPKeyboard] initWithSession:_session delegate:self];
193- (void)viewDidAppear:(BOOL)animated
195 [
super viewDidAppear:animated];
197 if (!_session_initilized)
199 if ([_session isSuspended])
202 [
self sessionBitmapContextDidChange:_session];
207 _session_initilized = YES;
211- (void)viewWillDisappear:(BOOL)animated
213 [
super viewWillDisappear:animated];
214 if (_mouse_drag_active)
215 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(NO)];
216 _mouse_drag_active = NO;
217 _mouse_pan_active = NO;
218 _long_press_active = NO;
220 [[
self navigationController] setNeedsStatusBarAppearanceUpdate];
221 [[
self navigationController] setNavigationBarHidden:NO animated:animated];
222 if (@available(iOS 18.0, *))
223 [[
self tabBarController] setTabBarHidden:NO animated:animated];
225 [[[
self tabBarController] tabBar] setHidden:NO];
231 [
self showSessionToolbar:NO];
232 [_dummy_textfield resignFirstResponder];
238 [[NSNotificationCenter defaultCenter] removeObserver:self];
241 [_session setDelegate:nil];
243 [_advanced_keyboard_view release];
244 [_keyboard_toolbar release];
250#pragma mark ScrollView delegate methods
252- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
254 return _session_view;
257- (void)scrollViewDidZoom:(UIScrollView *)scrollView
259 [
self centerSessionViewInViewport];
262- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView
263 withView:(UIView *)view
264 atScale:(CGFloat)scale
266 NSLog(
@"New zoom scale: %f", scale);
267 [
self centerSessionViewInViewport];
268 [_session_view setNeedsDisplayInRemoteRect:[_session_view bounds]];
272#pragma mark TextField delegate methods
273- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
275 _keyboard_visible = YES;
276 _advanced_keyboard_visible = NO;
280- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
282 _keyboard_visible = NO;
283 _advanced_keyboard_visible = NO;
287- (BOOL)textField:(UITextField *)textField
288 shouldChangeCharactersInRange:(NSRange)range
289 replacementString:(NSString *)string
291 if ([
string length] > 0)
293 for (
int i = 0; i < [string length]; i++)
295 unichar curChar = [string characterAtIndex:i];
299 [[
RDPKeyboard getSharedRDPKeyboard] sendEnterKeyStroke];
301 [[
RDPKeyboard getSharedRDPKeyboard] sendUnicode:curChar];
306 [[
RDPKeyboard getSharedRDPKeyboard] sendBackspaceKeyStroke];
313#pragma mark AdvancedKeyboardDelegate functions
314- (void)advancedKeyPressedVKey:(
int)key
316 [[
RDPKeyboard getSharedRDPKeyboard] sendVirtualKeyCode:key];
319- (void)advancedKeyPressedUnicode:(
int)key
321 [[
RDPKeyboard getSharedRDPKeyboard] sendUnicode:key];
324#pragma mark - RDP keyboard handler
326- (void)modifiersChangedForKeyboard:(
RDPKeyboard *)keyboard
328 UIBarButtonItem *curItem;
335 curItem = (UIBarButtonItem *)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
336 [curItem setStyle:[keyboard shiftPressed] ? UIBarButtonItemStyleDone
337 : UIBarButtonItemStyleBordered];
342 curItem = (UIBarButtonItem *)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
344 setStyle:[keyboard ctrlPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
348 curItem = (UIBarButtonItem *)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
350 setStyle:[keyboard winPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
354 curItem = (UIBarButtonItem *)[[_keyboard_toolbar items] objectAtIndex:objectIdx];
356 setStyle:[keyboard altPressed] ? UIBarButtonItemStyleDone : UIBarButtonItemStyleBordered];
360#pragma mark RDPSessionDelegate functions
362- (void)session:(
RDPSession *)session didFailToConnect:(
int)reason
365 [_connecting_indicator_view stopAnimating];
366 [_connecting_view removeFromSuperview];
367 [_connecting_view autorelease];
370 [[
self navigationController] popViewControllerAnimated:YES];
373- (void)sessionWillConnect:(
RDPSession *)session
376 [[NSBundle mainBundle] loadNibNamed:@"RDPConnectingView" owner:self options:nil];
379 [_lbl_connecting setText:NSLocalizedString(@"Connecting", @"Connecting progress view - label")];
380 [_cancel_connect_button setTitle:NSLocalizedString(@"Cancel", @"Cancel Button")
381 forState:UIControlStateNormal];
384 [_connecting_view setCenter:[[
self view] center]];
385 [[_connecting_view layer] setCornerRadius:10];
388 [[
self view] addSubview:_connecting_view];
389 [_connecting_indicator_view startAnimating];
392- (void)sessionDidConnect:(
RDPSession *)session
395 [[NSNotificationCenter defaultCenter] addObserver:self
396 selector:@selector(keyboardWillShow:)
397 name:UIKeyboardWillShowNotification
399 [[NSNotificationCenter defaultCenter] addObserver:self
400 selector:@selector(keyboardDidShow:)
401 name:UIKeyboardDidShowNotification
403 [[NSNotificationCenter defaultCenter] addObserver:self
404 selector:@selector(keyboardWillHide:)
405 name:UIKeyboardWillHideNotification
407 [[NSNotificationCenter defaultCenter] addObserver:self
408 selector:@selector(keyboardDidHide:)
409 name:UIKeyboardDidHideNotification
413 [[NSNotificationCenter defaultCenter] addObserver:self
414 selector:@selector(hardwareKeyboardChanged:)
415 name:GCKeyboardDidConnectNotification
417 [[NSNotificationCenter defaultCenter] addObserver:self
418 selector:@selector(hardwareKeyboardChanged:)
419 name:GCKeyboardDidDisconnectNotification
421 [
self hardwareKeyboardChanged:nil];
424 [_connecting_indicator_view stopAnimating];
425 [_connecting_view removeFromSuperview];
426 [_connecting_view autorelease];
432 rdpSettings *sess_params = [session getSessionParams];
437 if (([orig_params intForKey:
@"width"] != width &&
438 [orig_params intForKey:
@"width"] != (width + 1ul)) ||
439 [orig_params intForKey:
@"height"] != height || [orig_params intForKey:
@"colors"] != depth)
443 [NSString stringWithFormat:NSLocalizedString(
444 @"The server changed the screen settings to %dx%dx%d",
445 @"Screen settings not supported message with width, "
446 @"height and colors parameter"),
447 width, height, depth];
448 [[
self view] makeToast:message duration:ToastDurationNormal position:@"bottom"];
452- (void)sessionWillDisconnect:(
RDPSession *)session
456- (void)sessionDidDisconnect:(
RDPSession *)session
459 [[
self navigationController] popViewControllerAnimated:YES];
462- (void)sessionBitmapContextWillChange:(
RDPSession *)session
464 [_session_view prepareForBitmapContextChange];
467 rdpSettings *sess_params = [session getSessionParams];
473 [_session_scrollview setZoomScale:1.0];
474 [_session_view setFrame:view_rect];
475 [_session_scrollview setContentSize:view_rect.size];
476 _has_cursor_view_position = NO;
477 _has_user_moved_cursor = NO;
478 _last_session_viewport_size = CGSizeZero;
479 [
self fitSessionViewToViewport];
483 setToolbarVisible:![[NSUserDefaults standardUserDefaults] boolForKey:@"ui.hide_tool_bar"]];
484 [
self showSessionToolbar:[_session toolbarVisible]];
487- (void)sessionBitmapContextDidChange:(
RDPSession *)session
490 [_session_view setSession:session];
491 [_session_view setDefaultRemoteCursor];
492 if (!_has_cursor_view_position)
493 (void)[
self currentCursorViewPosition];
496 [_session_view setNeedsDisplayInRemoteRect:[_session_view bounds]];
499- (void)session:(
RDPSession *)session needsRedrawInRect:(CGRect)rect
501 [_session_view setNeedsDisplayInRemoteRect:rect];
507 [_session_view setRemoteCursor:cursor];
509 if (!_has_user_moved_cursor &&
510 (!_has_cursor_view_position || CGPointEqualToPoint(_cursor_view_position, CGPointZero)))
512 _has_cursor_view_position = NO;
513 (void)[
self currentCursorViewPosition];
517- (void)session:(
RDPSession *)session didMoveRemoteCursor:(CGPoint)position
520 if (_mouse_pan_active || _long_press_active)
522 CGPoint viewPosition = [
self sessionViewPositionForRemotePosition:position];
523 if (!_has_user_moved_cursor && CGPointEqualToPoint(position, CGPointZero) &&
524 _has_cursor_view_position && !CGPointEqualToPoint(_cursor_view_position, CGPointZero))
527 _cursor_view_position = viewPosition;
528 _has_cursor_view_position = YES;
529 [_session_view setRemoteCursorPosition:viewPosition];
532- (void)sessionDidHideRemoteCursor:(
RDPSession *)session
535 [_session_view hideRemoteCursor];
538- (void)sessionDidSetDefaultRemoteCursor:(
RDPSession *)session
541 [_session_view setDefaultRemoteCursor];
544- (void)session:(
RDPSession *)session requestsAuthenticationWithParams:(NSMutableDictionary *)params
550 params:params] autorelease];
551 [
self presentModalViewController:view_controller animated:YES];
554- (void)session:(
RDPSession *)session verifyCertificateWithParams:(NSMutableDictionary *)params
560 params:params] autorelease];
561 [
self presentModalViewController:view_controller animated:YES];
564- (CGSize)sizeForFitScreenForSession:(
RDPSession *)session
567 CGSize size = [
self view].bounds.size;
568 UIScreen *screen = [[
self view] window] ? [[[
self view] window] screen] : [UIScreen mainScreen];
570 [screen respondsToSelector:@selector(nativeScale)] ? [screen nativeScale] : [screen scale];
574 size.width = ceilf(size.width * scale);
575 size.height = ceilf(size.height * scale);
577 CGFloat maxDimension = MAX(size.width, size.height);
578 if (maxDimension > 4096.0f)
580 CGFloat downscale = 4096.0f / maxDimension;
581 size.width = floorf(size.width * downscale);
582 size.height = floorf(size.height * downscale);
585 size.width = MAX(64.0f, size.width);
586 size.height = MAX(64.0f, size.height);
590#pragma mark - Keyboard Toolbar Handlers
592- (void)showAdvancedKeyboardAnimated
595 CGRect rect = [[_keyboard_toolbar superview] bounds];
596 rect.origin.y = [_keyboard_toolbar bounds].size.height;
597 rect.size.height -= rect.origin.y;
601 initWithFrame:CGRectMake(rect.origin.x, [[_keyboard_toolbar superview] bounds].size.height,
602 rect.size.width, rect.size.height)
604 [[_keyboard_toolbar superview] addSubview:_advanced_keyboard_view];
607 [[_keyboard_toolbar superview] setAutoresizesSubviews:YES];
610 [UIView beginAnimations:nil context:NULL];
611 [_advanced_keyboard_view setFrame:rect];
612 [UIView commitAnimations];
615- (IBAction)toggleKeyboardWhenOtherVisible:(
id)sender
617 if (_advanced_keyboard_visible == NO)
619 [
self showAdvancedKeyboardAnimated];
624 [UIView beginAnimations:@"hide_advanced_keyboard_view" context:NULL];
625 CGRect rect = [_advanced_keyboard_view frame];
626 rect.origin.y = [[_keyboard_toolbar superview] bounds].size.height;
627 [_advanced_keyboard_view setFrame:rect];
628 [UIView commitAnimations];
634 _advanced_keyboard_visible = !_advanced_keyboard_visible;
637- (IBAction)toggleWinKey:(
id)sender
642- (IBAction)toggleShiftKey:(
id)sender
644 [[
RDPKeyboard getSharedRDPKeyboard] toggleShiftKey];
647- (IBAction)toggleCtrlKey:(
id)sender
649 [[
RDPKeyboard getSharedRDPKeyboard] toggleCtrlKey];
652- (IBAction)toggleAltKey:(
id)sender
657- (IBAction)pressEscKey:(
id)sender
659 [[
RDPKeyboard getSharedRDPKeyboard] sendEscapeKeyStroke];
663#pragma mark event handlers
665- (void)animationStopped:(NSString *)animationID
666 finished:(NSNumber *)finished
667 context:(
void *)context
669 if ([animationID isEqualToString:
@"hide_advanced_keyboard_view"])
672 [_advanced_keyboard_view removeFromSuperview];
673 [_advanced_keyboard_view autorelease];
674 _advanced_keyboard_view = nil;
678- (IBAction)switchSession:(
id)sender
680 [
self suspendSession];
683- (IBAction)toggleKeyboard:(
id)sender
685 if (!_keyboard_visible)
686 [_dummy_textfield becomeFirstResponder];
688 [_dummy_textfield resignFirstResponder];
691- (IBAction)toggleExtKeyboard:(
id)sender
694 if (_keyboard_visible && !_advanced_keyboard_visible)
695 [
self toggleKeyboardWhenOtherVisible:nil];
699 if (_advanced_keyboard_visible == NO)
700 _requesting_advanced_keyboard = YES;
701 [
self toggleKeyboard:nil];
705- (IBAction)disconnectSession:(
id)sender
707 [_session disconnect];
710- (IBAction)cancelButtonPressed:(
id)sender
712 [_session disconnect];
716#pragma mark iOS Keyboard Notification Handlers
718- (void)keyboardWillShow:(NSNotification *)notification
721 [
self centerSessionViewInViewport];
724- (void)keyboardDidShow:(NSNotification *)notification
726 if (_requesting_advanced_keyboard)
728 [
self showAdvancedKeyboardAnimated];
729 _advanced_keyboard_visible = YES;
730 _requesting_advanced_keyboard = NO;
734- (void)keyboardWillHide:(NSNotification *)notification
737 [
self centerSessionViewInViewport];
740- (void)keyboardDidHide:(NSNotification *)notification
743 if (_advanced_keyboard_visible == YES)
745 _advanced_keyboard_visible = NO;
746 [_advanced_keyboard_view removeFromSuperview];
747 [_advanced_keyboard_view autorelease];
748 _advanced_keyboard_view = nil;
752 if ([_session_view hardwareKeyboardActive])
753 [_session_view becomeFirstResponder];
756- (void)hardwareKeyboardChanged:(NSNotification *)notification
758 BOOL connected = (GCKeyboard.coalescedKeyboard != nil);
759 [_session_view setHardwareKeyboardActive:connected];
761 if (connected && !_keyboard_visible)
762 [_session_view becomeFirstResponder];
764 [_session_view resignFirstResponder];
768#pragma mark Gesture handlers
770- (void)handleSingleTap:(UITapGestureRecognizer *)gesture
772 if (_pointer_is_indirect)
773 [
self moveCursorToSessionViewPosition:[gesture locationInView:_session_view]];
774 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(YES)];
775 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(NO)];
778- (void)handleSecondaryTap:(UITapGestureRecognizer *)gesture
780 [
self moveCursorToSessionViewPosition:[gesture locationInView:_session_view]];
781 [
self sendMouseButtonEvent:GetRightMouseButtonClickEvent(YES)];
782 [
self sendMouseButtonEvent:GetRightMouseButtonClickEvent(NO)];
785- (void)handleLongPress:(UILongPressGestureRecognizer *)gesture
787 if ([gesture state] == UIGestureRecognizerStateBegan)
789 _long_press_active = YES;
790 _mouse_drag_active = NO;
792 else if ([gesture state] == UIGestureRecognizerStateEnded ||
793 [gesture state] == UIGestureRecognizerStateCancelled ||
794 [gesture state] == UIGestureRecognizerStateFailed)
796 if (_mouse_drag_active)
797 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(NO)];
798 else if ([gesture state] == UIGestureRecognizerStateEnded)
800 [
self sendMouseButtonEvent:GetRightMouseButtonClickEvent(YES)];
801 [
self sendMouseButtonEvent:GetRightMouseButtonClickEvent(NO)];
804 _mouse_drag_active = NO;
805 _long_press_active = NO;
809- (void)handleMousePan:(UIPanGestureRecognizer *)gesture
813 if (_pointer_is_indirect)
815 [
self moveCursorToSessionViewPosition:[gesture locationInView:_session_view]];
817 if ([gesture state] == UIGestureRecognizerStateBegan)
819 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(YES)];
820 _mouse_drag_active = YES;
822 else if ([gesture state] == UIGestureRecognizerStateEnded ||
823 [gesture state] == UIGestureRecognizerStateCancelled ||
824 [gesture state] == UIGestureRecognizerStateFailed)
826 if (_mouse_drag_active)
827 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(NO)];
828 _mouse_drag_active = NO;
833 CGPoint location = [gesture locationInView:_session_scrollview];
835 if ([gesture state] == UIGestureRecognizerStateBegan)
837 _mouse_pan_active = YES;
838 _last_mouse_pan_location = location;
839 if (_long_press_active && !_mouse_drag_active)
841 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(YES)];
842 _mouse_drag_active = YES;
845 else if ([gesture state] == UIGestureRecognizerStateChanged)
847 if (_long_press_active && !_mouse_drag_active)
849 [
self sendMouseButtonEvent:GetLeftMouseButtonClickEvent(YES)];
850 _mouse_drag_active = YES;
853 CGPoint delta = CGPointMake(location.x - _last_mouse_pan_location.x,
854 location.y - _last_mouse_pan_location.y);
855 [
self moveCursorByViewportDelta:delta];
856 _last_mouse_pan_location = location;
858 else if ([gesture state] == UIGestureRecognizerStateEnded ||
859 [gesture state] == UIGestureRecognizerStateCancelled ||
860 [gesture state] == UIGestureRecognizerStateFailed)
862 _mouse_pan_active = NO;
866- (void)handleHover:(UIHoverGestureRecognizer *)gesture
868 if ([gesture state] != UIGestureRecognizerStateBegan &&
869 [gesture state] != UIGestureRecognizerStateChanged)
872 [
self moveCursorToSessionViewPosition:[gesture locationInView:_session_view]];
875- (void)handleDoubleLongPress:(UILongPressGestureRecognizer *)gesture
879 CGPoint pos = [gesture locationInView:_session_scrollview];
880 CGPoint session_view_pos = [
self currentCursorViewPosition];
882 if ([gesture state] == UIGestureRecognizerStateBegan)
883 _prev_long_press_position = pos;
884 else if ([gesture state] == UIGestureRecognizerStateChanged)
886 int delta = _prev_long_press_position.y - pos.y;
888 if (delta > GetScrollGestureDelta())
890 [_session sendInputEvent:[
self eventDescriptorForMouseEvent:GetMouseWheelEvent(YES)
891 position:session_view_pos]];
892 _prev_long_press_position = pos;
894 else if (delta < -GetScrollGestureDelta())
896 [_session sendInputEvent:[
self eventDescriptorForMouseEvent:GetMouseWheelEvent(NO)
897 position:session_view_pos]];
898 _prev_long_press_position = pos;
903- (void)handleScroll:(UIPanGestureRecognizer *)gesture
905 CGFloat delta = [gesture translationInView:_session_view].y;
906 if (fabs(delta) < GetScrollGestureDelta())
909 CGPoint position = [
self currentCursorViewPosition];
910 [_session sendInputEvent:[
self eventDescriptorForMouseEvent:GetMouseWheelEvent(delta > 0)
912 [gesture setTranslation:CGPointZero inView:_session_view];
915- (void)handleSingle3FingersTap:(UITapGestureRecognizer *)gesture
917 [_session setToolbarVisible:![_session toolbarVisible]];
918 [
self showSessionToolbar:[_session toolbarVisible]];
921- (UIPointerStyle *)pointerInteraction:(UIPointerInteraction *)interaction
922 styleForRegion:(UIPointerRegion *)region
924 return [UIPointerStyle hiddenPointerStyle];
927- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
928 shouldReceiveTouch:(UITouch *)touch
931 if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
932 [(UIPanGestureRecognizer *)gestureRecognizer allowedScrollTypesMask] != 0)
935 _pointer_is_indirect = ([touch type] == UITouchTypeIndirectPointer);
939 if (_pointer_is_indirect &&
940 [gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]])
946- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
947 shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
950 ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] &&
951 [otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) ||
952 ([gestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]] &&
953 [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]);
959@implementation RDPSessionViewController (Private)
962#pragma mark Helper functions
964- (void)fitSessionViewToViewport
966 CGSize viewportSize = [_session_scrollview bounds].size;
967 CGSize remoteSize = [_session_view bounds].size;
968 if (viewportSize.width <= 0.0f || viewportSize.height <= 0.0f || remoteSize.width <= 0.0f ||
969 remoteSize.height <= 0.0f)
973 MIN(viewportSize.width / remoteSize.width, viewportSize.height / remoteSize.height);
974 [_session_scrollview setMinimumZoomScale:fitScale];
975 [_session_scrollview setMaximumZoomScale:MAX(2.0f, fitScale)];
976 [_session_scrollview setZoomScale:fitScale animated:NO];
977 [_session_scrollview setContentOffset:CGPointZero animated:NO];
978 _last_session_viewport_size = viewportSize;
979 [
self centerSessionViewInViewport];
982- (void)centerSessionViewInViewport
984 CGSize viewportSize = [_session_scrollview bounds].size;
985 CGRect sessionFrame = [_session_view frame];
986 if (viewportSize.width <= 0.0f || viewportSize.height <= 0.0f ||
987 sessionFrame.size.width <= 0.0f || sessionFrame.size.height <= 0.0f)
990 sessionFrame.origin.x = MAX((viewportSize.width - sessionFrame.size.width) * 0.5f, 0.0f);
991 sessionFrame.origin.y = MAX((viewportSize.height - sessionFrame.size.height) * 0.5f, 0.0f);
992 [_session_view setFrame:sessionFrame];
995 setContentSize:CGSizeMake(MAX(viewportSize.width, sessionFrame.size.width),
996 MAX(viewportSize.height, sessionFrame.size.height))];
999- (void)showSessionToolbar:(BOOL)show
1002 if (_session_toolbar_visible == show)
1008 UIEdgeInsets safe = [[
self view] safeAreaInsets];
1009 CGFloat toolbarWidth = [[
self view] bounds].size.width - safe.left - safe.right;
1013 [UIView beginAnimations:@"showToolbar" context:nil];
1014 [UIView setAnimationDuration:.4];
1015 [UIView setAnimationCurve:UIViewAnimationCurveLinear];
1016 [_session_toolbar setFrame:CGRectMake(safe.left, safe.top, toolbarWidth, TOOLBAR_HEIGHT)];
1017 [UIView commitAnimations];
1018 _session_toolbar_visible = YES;
1022 [UIView beginAnimations:@"hideToolbar" context:nil];
1023 [UIView setAnimationDuration:.4];
1024 [UIView setAnimationCurve:UIViewAnimationCurveLinear];
1026 setFrame:CGRectMake(safe.left, -TOOLBAR_HEIGHT, toolbarWidth, TOOLBAR_HEIGHT)];
1027 [UIView commitAnimations];
1028 _session_toolbar_visible = NO;
1032- (UIToolbar *)keyboardToolbar
1034 UIToolbar *keyboard_toolbar = [[[UIToolbar alloc] initWithFrame:CGRectNull] autorelease];
1035 [keyboard_toolbar setBarStyle:UIBarStyleBlackOpaque];
1037 UIBarButtonItem *esc_btn =
1038 [[[UIBarButtonItem alloc] initWithTitle:@"Esc"
1039 style:UIBarButtonItemStyleBordered
1041 action:@selector(pressEscKey:)] autorelease];
1043 [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"toolbar_icon_win"
1045 UIBarButtonItem *win_btn =
1046 [[[UIBarButtonItem alloc] initWithImage:win_icon
1047 style:UIBarButtonItemStyleBordered
1049 action:@selector(toggleWinKey:)] autorelease];
1050 UIBarButtonItem *ctrl_btn =
1051 [[[UIBarButtonItem alloc] initWithTitle:@"Ctrl"
1052 style:UIBarButtonItemStyleBordered
1054 action:@selector(toggleCtrlKey:)] autorelease];
1055 UIBarButtonItem *alt_btn =
1056 [[[UIBarButtonItem alloc] initWithTitle:@"Alt"
1057 style:UIBarButtonItemStyleBordered
1059 action:@selector(toggleAltKey:)] autorelease];
1060 UIBarButtonItem *ext_btn = [[[UIBarButtonItem alloc]
1061 initWithTitle:@"Ext"
1062 style:UIBarButtonItemStyleBordered
1064 action:@selector(toggleKeyboardWhenOtherVisible:)] autorelease];
1065 UIBarButtonItem *done_btn = [[[UIBarButtonItem alloc]
1066 initWithBarButtonSystemItem:UIBarButtonSystemItemDone
1068 action:@selector(toggleKeyboard:)] autorelease];
1069 UIBarButtonItem *flex_spacer =
1070 [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
1072 action:nil] autorelease];
1078 UIBarButtonItem *shift_btn =
1079 [[[UIBarButtonItem alloc] initWithTitle:@"Shift"
1080 style:UIBarButtonItemStyleBordered
1082 action:@selector(toggleShiftKey:)] autorelease];
1083 items = [NSArray arrayWithObjects:esc_btn, flex_spacer, shift_btn, flex_spacer, ctrl_btn,
1084 flex_spacer, win_btn, flex_spacer, alt_btn, flex_spacer,
1085 ext_btn, flex_spacer, done_btn, nil];
1089 items = [NSArray arrayWithObjects:esc_btn, flex_spacer, ctrl_btn, flex_spacer, win_btn,
1090 flex_spacer, alt_btn, flex_spacer, ext_btn, flex_spacer,
1094 [keyboard_toolbar setItems:items];
1095 [keyboard_toolbar sizeToFit];
1096 return keyboard_toolbar;
1099- (void)initGestureRecognizers
1103 UITapGestureRecognizer *singleTapRecognizer =
1104 [[[UITapGestureRecognizer alloc] initWithTarget:self
1105 action:@selector(handleSingleTap:)] autorelease];
1106 [singleTapRecognizer setNumberOfTouchesRequired:1];
1107 [singleTapRecognizer setNumberOfTapsRequired:1];
1108 [singleTapRecognizer setDelegate:self];
1111 UILongPressGestureRecognizer *longPressRecognizer = [[[UILongPressGestureRecognizer alloc]
1113 action:@selector(handleLongPress:)] autorelease];
1114 [longPressRecognizer setMinimumPressDuration:0.45];
1115 [longPressRecognizer setAllowableMovement:12.0];
1116 [longPressRecognizer setDelegate:self];
1119 UIPanGestureRecognizer *mousePanRecognizer =
1120 [[[UIPanGestureRecognizer alloc] initWithTarget:self
1121 action:@selector(handleMousePan:)] autorelease];
1122 [mousePanRecognizer setMinimumNumberOfTouches:1];
1123 [mousePanRecognizer setMaximumNumberOfTouches:1];
1124 [mousePanRecognizer setDelegate:self];
1127 UIHoverGestureRecognizer *hoverRecognizer =
1128 [[[UIHoverGestureRecognizer alloc] initWithTarget:self
1129 action:@selector(handleHover:)] autorelease];
1130 [hoverRecognizer setDelegate:self];
1134 UITapGestureRecognizer *secondaryTapRecognizer = [[[UITapGestureRecognizer alloc]
1136 action:@selector(handleSecondaryTap:)] autorelease];
1137 [secondaryTapRecognizer setButtonMaskRequired:UIEventButtonMaskSecondary];
1138 [secondaryTapRecognizer setAllowedTouchTypes:@[@(UITouchTypeIndirectPointer)]];
1139 [secondaryTapRecognizer setDelegate:self];
1143 UIPanGestureRecognizer *scrollRecognizer =
1144 [[[UIPanGestureRecognizer alloc] initWithTarget:self
1145 action:@selector(handleScroll:)] autorelease];
1146 [scrollRecognizer setAllowedScrollTypesMask:UIScrollTypeMaskAll];
1147 [scrollRecognizer setDelegate:self];
1150 UILongPressGestureRecognizer *doubleLongPressRecognizer = [[[UILongPressGestureRecognizer alloc]
1152 action:@selector(handleDoubleLongPress:)] autorelease];
1153 [doubleLongPressRecognizer setNumberOfTouchesRequired:2];
1154 [doubleLongPressRecognizer setMinimumPressDuration:0.5];
1157 UITapGestureRecognizer *single3FingersTapRecognizer = [[[UITapGestureRecognizer alloc]
1159 action:@selector(handleSingle3FingersTap:)] autorelease];
1160 [single3FingersTapRecognizer setNumberOfTapsRequired:1];
1161 [single3FingersTapRecognizer setNumberOfTouchesRequired:3];
1162 [singleTapRecognizer requireGestureRecognizerToFail:longPressRecognizer];
1165 [[_session_scrollview panGestureRecognizer] setMinimumNumberOfTouches:2];
1168 [_session_scrollview addGestureRecognizer:singleTapRecognizer];
1169 [_session_scrollview addGestureRecognizer:longPressRecognizer];
1170 [_session_scrollview addGestureRecognizer:mousePanRecognizer];
1171 [_session_scrollview addGestureRecognizer:hoverRecognizer];
1172 [_session_scrollview addGestureRecognizer:secondaryTapRecognizer];
1173 [_session_scrollview addGestureRecognizer:scrollRecognizer];
1174 [_session_scrollview addGestureRecognizer:doubleLongPressRecognizer];
1175 [_session_scrollview addGestureRecognizer:single3FingersTapRecognizer];
1178 UIPointerInteraction *pointerInteraction =
1179 [[[UIPointerInteraction alloc] initWithDelegate:self] autorelease];
1180 [_session_view addInteraction:pointerInteraction];
1183- (void)suspendSession
1189 [[
self navigationController] popViewControllerAnimated:YES];
1192- (NSDictionary *)eventDescriptorForMouseEvent:(
int)event position:(CGPoint)position
1194 CGPoint remote_position = [
self remotePositionForSessionViewPosition:position];
1195 return [NSDictionary
1196 dictionaryWithObjectsAndKeys:@"mouse", @"type", [NSNumber numberWithUnsignedShort:event],
1198 [NSNumber numberWithUnsignedShort:lrintf(remote_position.x)],
1200 [NSNumber numberWithUnsignedShort:lrintf(remote_position.y)],
1204- (CGPoint)remotePositionForSessionViewPosition:(CGPoint)position
1206 rdpSettings *settings = [_session getSessionParams];
1207 CGSize viewSize = [_session_view bounds].size;
1208 CGFloat desktopWidth =
1210 CGFloat desktopHeight =
1213 if ((viewSize.width > 0.0f) && (desktopWidth > 0.0f))
1214 position.x = position.x * desktopWidth / viewSize.width;
1215 if ((viewSize.height > 0.0f) && (desktopHeight > 0.0f))
1216 position.y = position.y * desktopHeight / viewSize.height;
1218 if (desktopWidth > 0.0f)
1219 position.x = MIN(MAX(position.x, 0.0f), desktopWidth - 1.0f);
1220 if (desktopHeight > 0.0f)
1221 position.y = MIN(MAX(position.y, 0.0f), desktopHeight - 1.0f);
1226- (CGPoint)sessionViewPositionForRemotePosition:(CGPoint)position
1228 rdpSettings *settings = [_session getSessionParams];
1229 CGSize viewSize = [_session_view bounds].size;
1230 CGFloat desktopWidth =
1232 CGFloat desktopHeight =
1235 if ((desktopWidth > 0.0f) && (viewSize.width > 0.0f))
1236 position.x = position.x * viewSize.width / desktopWidth;
1237 if ((desktopHeight > 0.0f) && (viewSize.height > 0.0f))
1238 position.y = position.y * viewSize.height / desktopHeight;
1240 return [
self clampedSessionViewCursorPosition:position];
1243- (CGPoint)clampedSessionViewCursorPosition:(CGPoint)position
1245 CGSize viewSize = [_session_view bounds].size;
1246 if (viewSize.width > 0.0f)
1247 position.x = MIN(MAX(position.x, 0.0f), viewSize.width - 1.0f);
1248 if (viewSize.height > 0.0f)
1249 position.y = MIN(MAX(position.y, 0.0f), viewSize.height - 1.0f);
1253- (CGPoint)currentCursorViewPosition
1255 if (!_has_cursor_view_position)
1257 CGSize viewSize = [_session_view bounds].size;
1258 _cursor_view_position = CGPointMake(MAX(viewSize.width - 1.0, 0.0) * 0.5,
1259 MAX(viewSize.height - 1.0, 0.0) * 0.5);
1260 _has_cursor_view_position = YES;
1261 [_session_view setRemoteCursorPosition:_cursor_view_position];
1263 [_session_view showRemoteCursor];
1265 return _cursor_view_position;
1268- (void)moveCursorByViewportDelta:(CGPoint)delta
1270 CGPoint position = [
self currentCursorViewPosition];
1271 CGFloat zoomScale = [_session_scrollview zoomScale];
1272 if (zoomScale <= 0.0)
1275 position.x += delta.x / zoomScale;
1276 position.y += delta.y / zoomScale;
1277 position = [
self clampedSessionViewCursorPosition:position];
1278 _cursor_view_position = position;
1279 _has_cursor_view_position = YES;
1280 _has_user_moved_cursor = YES;
1283 [_session_view setRemoteCursorPosition:position];
1284 [
self handleMouseMoveForPosition:position];
1287- (void)moveCursorToSessionViewPosition:(CGPoint)position
1289 position = [
self clampedSessionViewCursorPosition:position];
1290 _cursor_view_position = position;
1291 _has_cursor_view_position = YES;
1292 _has_user_moved_cursor = YES;
1295 [_session_view setRemoteCursorPosition:position];
1296 [_session_view showRemoteCursor];
1297 [
self handleMouseMoveForPosition:position];
1300- (void)sendMouseButtonEvent:(
int)event
1302 CGPoint position = [
self currentCursorViewPosition];
1303 [_session sendInputEvent:[
self eventDescriptorForMouseEvent:event position:position]];
1306- (void)handleMouseMoveForPosition:(CGPoint)position
1308 [_session sendInputEvent:[
self eventDescriptorForMouseEvent:PTR_FLAGS_MOVE position:position]];
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.