2014-08-31 13:13:46 -04:00
//
// StreamFrameViewController . m
2015-05-12 21:48:30 -04:00
// Moonlight
2014-08-31 13:13:46 -04:00
//
// Created by Diego Waxemberg on 1 / 18 / 14.
2015-05-12 21:48:30 -04:00
// Copyright ( c ) 2015 Moonlight Stream . All rights reserved .
2014-08-31 13:13:46 -04:00
//
# import "StreamFrameViewController.h"
# import "MainFrameViewController.h"
2014-10-18 22:43:52 -04:00
# import "VideoDecoderRenderer.h"
2014-10-20 19:20:50 -04:00
# import "StreamManager.h"
2014-10-20 21:13:13 -04:00
# import "ControllerSupport.h"
2020-11-01 13:27:10 -06:00
# import "DataManager.h"
2014-08-31 13:13:46 -04:00
# include < sys / socket . h >
# include < netinet / in . h >
# include < arpa / inet . h >
2019-07-23 18:43:41 -04:00
# include < Limelight . h >
2014-08-31 13:13:46 -04:00
2022-02-13 14:27:17 -06:00
# if TARGET_OS _TV
# import < AVFoundation / AVDisplayCriteria . h >
# import < AVKit / AVDisplayManager . h >
# import < AVKit / UIWindow . h >
# endif
@ interface AVDisplayCriteria ( )
@ property ( readonly ) int videoDynamicRange ;
@ property ( readonly , nonatomic ) float refreshRate ;
- ( id ) initWithRefreshRate : ( float ) arg1 videoDynamicRange : ( int ) arg2 ;
@ end
2014-10-20 21:13:13 -04:00
@ implementation StreamFrameViewController {
ControllerSupport * _controllerSupport ;
2014-10-21 01:54:31 -04:00
StreamManager * _streamMan ;
2020-11-01 20:22:01 -06:00
TemporarySettings * _settings ;
2018-06-09 16:29:48 -07:00
NSTimer * _inactivityTimer ;
2020-11-01 13:27:10 -06:00
NSTimer * _statsUpdateTimer ;
2021-05-06 23:42:52 -05:00
UITapGestureRecognizer * _menuTapGestureRecognizer ;
2018-10-23 23:18:56 -04:00
UITapGestureRecognizer * _menuDoubleTapGestureRecognizer ;
2021-05-06 23:42:52 -05:00
UITapGestureRecognizer * _playPauseTapGestureRecognizer ;
2019-07-23 18:43:41 -04:00
UITextView * _overlayView ;
2020-11-01 20:00:39 -06:00
UILabel * _stageLabel ;
UILabel * _tipLabel ;
UIActivityIndicatorView * _spinner ;
2019-10-22 00:16:20 -04:00
StreamView * _streamView ;
2020-11-01 20:22:01 -06:00
UIScrollView * _scrollView ;
2019-11-02 15:17:52 -07:00
BOOL _userIsInteracting ;
2020-11-02 18:41:38 -06:00
CGSize _keyboardSize ;
2020-11-01 21:18:06 -06:00
# if ! TARGET_OS _TV
UIScreenEdgePanGestureRecognizer * _exitSwipeRecognizer ;
# endif
2014-10-20 21:13:13 -04:00
}
2014-08-31 13:13:46 -04:00
2017-09-15 00:30:51 -07:00
- ( void ) viewDidAppear : ( BOOL ) animated
{
[ super viewDidAppear : animated ] ;
2018-08-26 11:31:20 -07:00
# if ! TARGET_OS _TV
2017-09-15 00:30:51 -07:00
[ [ self revealViewController ] setPrimaryViewController : self ] ;
2018-08-26 11:31:20 -07:00
# endif
2017-09-15 00:30:51 -07:00
}
2019-10-12 19:20:26 -07:00
# if TARGET_OS _TV
2018-10-23 23:18:56 -04:00
- ( void ) controllerPauseButtonPressed : ( id ) sender { }
- ( void ) controllerPauseButtonDoublePressed : ( id ) sender {
Log ( LOG_I , @ "Menu double-pressed -- backing out of stream" ) ;
[ self returnToMainFrame ] ;
}
2021-05-06 23:42:52 -05:00
- ( void ) controllerPlayPauseButtonPressed : ( id ) sender {
Log ( LOG_I , @ "Play/Pause button pressed -- backing out of stream" ) ;
[ self returnToMainFrame ] ;
}
2018-10-23 23:18:56 -04:00
# endif
2014-08-31 13:13:46 -04:00
- ( void ) viewDidLoad
{
[ super viewDidLoad ] ;
2014-10-21 17:18:30 -04:00
2014-10-29 00:47:51 -04:00
[ self . navigationController setNavigationBarHidden : YES animated : YES ] ;
2014-08-31 13:13:46 -04:00
[ UIApplication sharedApplication ] . idleTimerDisabled = YES ;
2014-10-19 00:09:47 -04:00
2020-11-01 20:22:01 -06:00
_settings = [ [ [ DataManager alloc ] init ] getSettings ] ;
2020-11-01 20:00:39 -06:00
_stageLabel = [ [ UILabel alloc ] init ] ;
[ _stageLabel setUserInteractionEnabled : NO ] ;
[ _stageLabel setText : [ NSString stringWithFormat : @ "Starting %@..." , self . streamConfig . appName ] ] ;
[ _stageLabel sizeToFit ] ;
_stageLabel . textAlignment = NSTextAlignmentCenter ;
_stageLabel . textColor = [ UIColor whiteColor ] ;
_stageLabel . center = CGPointMake ( self . view . frame . size . width / 2 , self . view . frame . size . height / 2 ) ;
_spinner = [ [ UIActivityIndicatorView alloc ] init ] ;
[ _spinner setUserInteractionEnabled : NO ] ;
# if TARGET_OS _TV
[ _spinner setActivityIndicatorViewStyle : UIActivityIndicatorViewStyleWhiteLarge ] ;
# else
[ _spinner setActivityIndicatorViewStyle : UIActivityIndicatorViewStyleWhite ] ;
# endif
[ _spinner sizeToFit ] ;
[ _spinner startAnimating ] ;
_spinner . center = CGPointMake ( self . view . frame . size . width / 2 , self . view . frame . size . height / 2 - _stageLabel . frame . size . height - _spinner . frame . size . height ) ;
2019-10-22 00:32:56 -04:00
_controllerSupport = [ [ ControllerSupport alloc ] initWithConfig : self . streamConfig presenceDelegate : self ] ;
2018-06-09 16:29:48 -07:00
_inactivityTimer = nil ;
2019-10-22 00:16:20 -04:00
2020-11-01 20:00:39 -06:00
_streamView = [ [ StreamView alloc ] initWithFrame : self . view . frame ] ;
2020-11-01 21:18:06 -06:00
[ _streamView setupStreamView : _controllerSupport interactionDelegate : self config : self . streamConfig ] ;
2019-10-22 00:16:20 -04:00
2018-10-23 23:18:56 -04:00
# if TARGET_OS _TV
2021-05-06 23:42:52 -05:00
if ( ! _menuTapGestureRecognizer || ! _menuDoubleTapGestureRecognizer || ! _playPauseTapGestureRecognizer ) {
_menuTapGestureRecognizer = [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( controllerPauseButtonPressed : ) ] ;
_menuTapGestureRecognizer . allowedPressTypes = @ [ @ ( UIPressTypeMenu ) ] ;
2018-10-23 23:18:56 -04:00
2021-05-06 23:42:52 -05:00
_playPauseTapGestureRecognizer = [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( controllerPlayPauseButtonPressed : ) ] ;
_playPauseTapGestureRecognizer . allowedPressTypes = @ [ @ ( UIPressTypePlayPause ) ] ;
2018-10-23 23:18:56 -04:00
_menuDoubleTapGestureRecognizer = [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( controllerPauseButtonDoublePressed : ) ] ;
_menuDoubleTapGestureRecognizer . numberOfTapsRequired = 2 ;
2021-05-06 23:42:52 -05:00
[ _menuTapGestureRecognizer requireGestureRecognizerToFail : _menuDoubleTapGestureRecognizer ] ;
2018-10-23 23:18:56 -04:00
_menuDoubleTapGestureRecognizer . allowedPressTypes = @ [ @ ( UIPressTypeMenu ) ] ;
}
2021-05-06 23:42:52 -05:00
[ self . view addGestureRecognizer : _menuTapGestureRecognizer ] ;
2018-10-23 23:18:56 -04:00
[ self . view addGestureRecognizer : _menuDoubleTapGestureRecognizer ] ;
2021-05-06 23:42:52 -05:00
[ self . view addGestureRecognizer : _playPauseTapGestureRecognizer ] ;
2020-11-01 21:18:06 -06:00
# else
_exitSwipeRecognizer = [ [ UIScreenEdgePanGestureRecognizer alloc ] initWithTarget : self action : @ selector ( edgeSwiped ) ] ;
_exitSwipeRecognizer . edges = UIRectEdgeLeft ;
2020-11-02 18:41:38 -06:00
_exitSwipeRecognizer . delaysTouchesBegan = NO ;
_exitSwipeRecognizer . delaysTouchesEnded = NO ;
2018-12-22 21:43:37 -08:00
2020-11-01 21:18:06 -06:00
[ self . view addGestureRecognizer : _exitSwipeRecognizer ] ;
# endif
2020-11-01 20:00:39 -06:00
_tipLabel = [ [ UILabel alloc ] init ] ;
[ _tipLabel setUserInteractionEnabled : NO ] ;
2018-12-22 21:43:37 -08:00
# if TARGET_OS _TV
2021-05-06 23:42:52 -05:00
[ _tipLabel setText : @ "Tip: Tap the Play/Pause button on the Apple TV Remote to disconnect from your PC" ] ;
2018-12-22 21:43:37 -08:00
# else
2020-11-01 20:00:39 -06:00
[ _tipLabel setText : @ "Tip: Swipe from the left edge to disconnect from your PC" ] ;
2018-12-22 21:43:37 -08:00
# endif
2020-11-01 20:00:39 -06:00
[ _tipLabel sizeToFit ] ;
_tipLabel . textColor = [ UIColor whiteColor ] ;
_tipLabel . textAlignment = NSTextAlignmentCenter ;
_tipLabel . center = CGPointMake ( self . view . frame . size . width / 2 , self . view . frame . size . height * 0.9 ) ;
2018-12-22 21:43:37 -08:00
2015-02-01 19:46:28 -05:00
_streamMan = [ [ StreamManager alloc ] initWithConfig : self . streamConfig
2020-11-01 20:00:39 -06:00
renderView : _streamView
2014-10-21 14:54:10 -04:00
connectionCallbacks : self ] ;
2014-08-31 13:13:46 -04:00
NSOperationQueue * opQueue = [ [ NSOperationQueue alloc ] init ] ;
2014-10-21 01:54:31 -04:00
[ opQueue addOperation : _streamMan ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( applicationWillResignActive : )
name : UIApplicationWillResignActiveNotification
object : nil ] ;
2018-06-09 16:29:48 -07:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( applicationDidBecomeActive : )
name : UIApplicationDidBecomeActiveNotification
object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( applicationDidEnterBackground : )
name : UIApplicationDidEnterBackgroundNotification
object : nil ] ;
2020-11-02 18:41:38 -06:00
2020-11-28 18:50:54 -06:00
# if 0
// FIXME : This doesn ' t work reliably on iPad for some reason . Showing and hiding the keyboard
// several times in a row will not correctly restore the state of the UIScrollView .
2020-11-02 18:41:38 -06:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( keyboardWillShow : )
name : UIKeyboardWillShowNotification
object : nil ] ;
[ [ NSNotificationCenter defaultCenter ] addObserver : self
selector : @ selector ( keyboardWillHide : )
name : UIKeyboardWillHideNotification
object : nil ] ;
2020-11-02 20:39:37 -06:00
# endif
2020-11-01 20:00:39 -06:00
2020-11-01 20:22:01 -06:00
// Only enable scroll and zoom in absolute touch mode
if ( _settings . absoluteTouchMode ) {
_scrollView = [ [ UIScrollView alloc ] initWithFrame : self . view . frame ] ;
# if ! TARGET_OS _TV
[ _scrollView . panGestureRecognizer setMinimumNumberOfTouches : 2 ] ;
# endif
[ _scrollView setShowsHorizontalScrollIndicator : NO ] ;
[ _scrollView setShowsVerticalScrollIndicator : NO ] ;
[ _scrollView setDelegate : self ] ;
[ _scrollView setMaximumZoomScale : 10.0 f ] ;
// Add StreamView inside a UIScrollView for absolute mode
[ _scrollView addSubview : _streamView ] ;
[ self . view addSubview : _scrollView ] ;
}
else {
// Add StreamView directly in relative mode
[ self . view addSubview : _streamView ] ;
}
2020-11-01 20:00:39 -06:00
[ self . view addSubview : _stageLabel ] ;
[ self . view addSubview : _spinner ] ;
[ self . view addSubview : _tipLabel ] ;
2018-06-09 16:29:48 -07:00
}
2020-11-01 20:22:01 -06:00
- ( UIView * ) viewForZoomingInScrollView : ( UIScrollView * ) scrollView {
return _streamView ;
}
2018-08-29 09:21:49 -07:00
- ( void ) willMoveToParentViewController : ( UIViewController * ) parent {
2018-12-28 02:03:21 -08:00
// Only cleanup when we ' re being destroyed
if ( parent = = nil ) {
[ _controllerSupport cleanup ] ;
[ UIApplication sharedApplication ] . idleTimerDisabled = NO ;
[ _streamMan stopStream ] ;
if ( _inactivityTimer ! = nil ) {
[ _inactivityTimer invalidate ] ;
_inactivityTimer = nil ;
}
[ [ NSNotificationCenter defaultCenter ] removeObserver : self ] ;
2018-06-09 16:29:48 -07:00
}
2014-10-21 01:54:31 -04:00
}
2020-11-28 18:50:54 -06:00
# if 0
2020-11-02 18:41:38 -06:00
- ( void ) keyboardWillShow : ( NSNotification * ) notification {
_keyboardSize = [ [ [ notification userInfo ] objectForKey : UIKeyboardFrameBeginUserInfoKey ] CGRectValue ] . size ;
[ UIView animateWithDuration : 0.3 animations : ^ {
CGRect frame = self -> _scrollView . frame ;
frame . size . height - = self -> _keyboardSize . height ;
self -> _scrollView . frame = frame ;
} ] ;
}
- ( void ) keyboardWillHide : ( NSNotification * ) notification {
// NOTE : UIKeyboardFrameEndUserInfoKey returns a different keyboard size
// than UIKeyboardFrameBeginUserInfoKey , so it ' s unsuitable for use here
// to undo the changes made by keyboardWillShow .
[ UIView animateWithDuration : 0.3 animations : ^ {
CGRect frame = self -> _scrollView . frame ;
frame . size . height + = self -> _keyboardSize . height ;
self -> _scrollView . frame = frame ;
} ] ;
}
2020-11-02 20:39:37 -06:00
# endif
2020-11-02 18:41:38 -06:00
2020-11-01 13:27:10 -06:00
- ( void ) updateStatsOverlay {
NSString * overlayText = [ self -> _streamMan getStatsOverlayText ] ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self updateOverlayText : overlayText ] ;
} ) ;
}
2019-07-23 18:43:41 -04:00
- ( void ) updateOverlayText : ( NSString * ) text {
if ( _overlayView = = nil ) {
_overlayView = [ [ UITextView alloc ] init ] ;
2019-10-12 19:20:26 -07:00
# if ! TARGET_OS _TV
2019-07-23 18:43:41 -04:00
[ _overlayView setEditable : NO ] ;
2019-08-07 00:53:58 -07:00
# endif
[ _overlayView setUserInteractionEnabled : NO ] ;
2019-07-23 18:43:41 -04:00
[ _overlayView setSelectable : NO ] ;
[ _overlayView setScrollEnabled : NO ] ;
2020-11-01 13:27:10 -06:00
// HACK : If not using stats overlay , center the text
if ( _statsUpdateTimer = = nil ) {
[ _overlayView setTextAlignment : NSTextAlignmentCenter ] ;
}
2020-11-01 16:50:02 -06:00
[ _overlayView setTextColor : [ UIColor lightGrayColor ] ] ;
[ _overlayView setBackgroundColor : [ UIColor blackColor ] ] ;
2019-09-24 17:36:26 -07:00
# if TARGET_OS _TV
[ _overlayView setFont : [ UIFont systemFontOfSize : 24 ] ] ;
# else
2019-07-23 18:43:41 -04:00
[ _overlayView setFont : [ UIFont systemFontOfSize : 12 ] ] ;
2019-09-24 17:36:26 -07:00
# endif
2019-07-23 18:43:41 -04:00
[ _overlayView setAlpha : 0.5 ] ;
[ self . view addSubview : _overlayView ] ;
}
if ( text ! = nil ) {
2020-11-01 13:45:24 -06:00
// We set our bounds to the maximum width in order to work around a bug where
// sizeToFit interacts badly with the UITextView ' s line breaks , causing the
// width to get smaller and smaller each time as more line breaks are inserted .
[ _overlayView setBounds : CGRectMake ( self . view . frame . origin . x ,
_overlayView . frame . origin . y ,
self . view . frame . size . width ,
_overlayView . frame . size . height ) ] ;
2019-07-23 18:43:41 -04:00
[ _overlayView setText : text ] ;
[ _overlayView sizeToFit ] ;
[ _overlayView setCenter : CGPointMake ( self . view . frame . size . width / 2 , _overlayView . frame . size . height / 2 ) ] ;
[ _overlayView setHidden : NO ] ;
}
else {
[ _overlayView setHidden : YES ] ;
}
}
2014-10-29 00:47:51 -04:00
- ( void ) returnToMainFrame {
2022-02-13 14:27:17 -06:00
// Reset display mode back to default
[ self updatePreferredDisplayMode : NO ] ;
2020-11-01 13:27:10 -06:00
[ _statsUpdateTimer invalidate ] ;
_statsUpdateTimer = nil ;
2014-10-29 00:47:51 -04:00
[ self . navigationController popToRootViewControllerAnimated : YES ] ;
}
2018-06-09 16:29:48 -07:00
// This will fire if the user opens control center or gets a low battery message
2014-10-21 01:54:31 -04:00
- ( void ) applicationWillResignActive : ( NSNotification * ) notification {
2018-06-09 16:29:48 -07:00
if ( _inactivityTimer ! = nil ) {
[ _inactivityTimer invalidate ] ;
}
2020-10-18 17:43:34 -05:00
# if ! TARGET_OS _TV
2020-10-18 17:44:52 -05:00
// Terminate the stream if the app is inactive for 60 seconds
2018-06-09 16:29:48 -07:00
Log ( LOG_I , @ "Starting inactivity termination timer" ) ;
2020-10-18 17:44:52 -05:00
_inactivityTimer = [ NSTimer scheduledTimerWithTimeInterval : 60
2018-06-09 16:29:48 -07:00
target : self
selector : @ selector ( inactiveTimerExpired : )
userInfo : nil
repeats : NO ] ;
2019-09-24 17:37:35 -07:00
# endif
2018-06-09 16:29:48 -07:00
}
- ( void ) inactiveTimerExpired : ( NSTimer * ) timer {
Log ( LOG_I , @ "Terminating stream after inactivity" ) ;
[ self returnToMainFrame ] ;
_inactivityTimer = nil ;
}
- ( void ) applicationDidBecomeActive : ( NSNotification * ) notification {
// Stop the background timer , since we ' re foregrounded again
if ( _inactivityTimer ! = nil ) {
Log ( LOG_I , @ "Stopping inactivity timer after becoming active again" ) ;
[ _inactivityTimer invalidate ] ;
_inactivityTimer = nil ;
}
}
// This fires when the home button is pressed
- ( void ) applicationDidEnterBackground : ( UIApplication * ) application {
Log ( LOG_I , @ "Terminating stream immediately for backgrounding" ) ;
if ( _inactivityTimer ! = nil ) {
[ _inactivityTimer invalidate ] ;
_inactivityTimer = nil ;
}
2014-10-29 00:47:51 -04:00
[ self returnToMainFrame ] ;
2014-08-31 13:13:46 -04:00
}
2015-01-09 15:22:12 -05:00
- ( void ) edgeSwiped {
2018-06-09 16:29:48 -07:00
Log ( LOG_I , @ "User swiped to end stream" ) ;
2015-01-09 15:22:12 -05:00
[ self returnToMainFrame ] ;
}
2014-10-21 14:54:10 -04:00
- ( void ) connectionStarted {
2015-02-10 21:26:55 -05:00
Log ( LOG_I , @ "Connection started" ) ;
2014-10-21 17:18:30 -04:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2018-06-02 01:02:10 -07:00
// Leave the spinner spinning until it ' s obscured by
// the first frame of video .
2020-11-01 20:00:39 -06:00
self -> _stageLabel . hidden = YES ;
self -> _tipLabel . hidden = YES ;
2019-10-22 00:16:20 -04:00
[ self -> _streamView showOnScreenControls ] ;
2020-11-01 13:27:10 -06:00
2020-11-01 20:22:01 -06:00
if ( self -> _settings . statsOverlay ) {
2020-11-01 13:27:10 -06:00
self -> _statsUpdateTimer = [ NSTimer scheduledTimerWithTimeInterval : 1.0 f
target : self
selector : @ selector ( updateStatsOverlay )
userInfo : nil
repeats : YES ] ;
}
2014-10-21 17:18:30 -04:00
} ) ;
2014-10-21 14:54:10 -04:00
}
2020-02-25 00:00:00 -08:00
- ( void ) connectionTerminated : ( int ) errorCode {
Log ( LOG_I , @ "Connection terminated: %d" , errorCode ) ;
2014-10-21 14:54:10 -04:00
2020-12-23 14:48:20 -06:00
unsigned int portFlags = LiGetPortFlagsFromTerminationErrorCode ( errorCode ) ;
unsigned int portTestResults = LiTestClientConnectivity ( CONN_TEST _SERVER , 443 , portFlags ) ;
2020-08-10 22:56:47 -07:00
2015-01-09 17:54:21 -05:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2018-05-30 22:04:06 -07:00
// Allow the display to go to sleep now
[ UIApplication sharedApplication ] . idleTimerDisabled = NO ;
2020-05-01 19:37:33 -07:00
NSString * title ;
NSString * message ;
2020-08-10 22:56:47 -07:00
if ( portTestResults ! = ML_TEST _RESULT _INCONCLUSIVE && portTestResults ! = 0 ) {
title = @ "Connection Error" ;
2020-08-30 15:37:52 -07:00
message = @ "Your device's network connection is blocking Moonlight. Streaming may not work while connected to this network." ;
2020-08-10 22:56:47 -07:00
}
else {
switch ( errorCode ) {
case ML_ERROR _GRACEFUL _TERMINATION :
[ self returnToMainFrame ] ;
return ;
case ML_ERROR _NO _VIDEO _TRAFFIC :
title = @ "Connection Error" ;
2020-12-23 14:48:20 -06:00
message = @ "No video received from host." ;
if ( portFlags ! = 0 ) {
char failingPorts [ 256 ] ;
LiStringifyPortFlags ( portFlags , "\n" , failingPorts , sizeof ( failingPorts ) ) ;
message = [ message stringByAppendingString : [ NSString stringWithFormat : @ "\n\nCheck your firewall and port forwarding rules for port(s):\n%s" , failingPorts ] ] ;
}
2020-08-10 22:56:47 -07:00
break ;
2020-08-29 21:21:46 -07:00
case ML_ERROR _NO _VIDEO _FRAME :
title = @ "Connection Error" ;
message = @ "Your network connection isn't performing well. Reduce your video bitrate setting or try a faster connection." ;
break ;
2020-12-24 12:48:10 -06:00
case ML_ERROR _UNEXPECTED _EARLY _TERMINATION :
2021-04-09 19:37:27 -05:00
case ML_ERROR _PROTECTED _CONTENT :
2020-12-24 12:48:10 -06:00
title = @ "Connection Error" ;
message = @ "Something went wrong on your host PC when starting the stream.\n\nMake sure you don't have any DRM-protected content open on your host PC. You can also try restarting your host PC.\n\nIf the issue persists, try reinstalling your GPU drivers and GeForce Experience." ;
break ;
2020-08-10 22:56:47 -07:00
default :
title = @ "Connection Terminated" ;
message = @ "The connection was terminated" ;
break ;
}
2019-02-14 00:03:09 -05:00
}
2020-05-01 19:37:33 -07:00
UIAlertController * conTermAlert = [ UIAlertController alertControllerWithTitle : title
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
[ Utils addHelpOptionToDialog : conTermAlert ] ;
[ conTermAlert addAction : [ UIAlertAction actionWithTitle : @ "OK" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
[ self returnToMainFrame ] ;
} ] ] ;
[ self presentViewController : conTermAlert animated : YES completion : nil ] ;
2015-01-09 17:54:21 -05:00
} ) ;
2016-04-12 00:50:59 -04:00
[ _streamMan stopStream ] ;
2014-10-21 01:02:04 -04:00
}
2016-07-31 17:10:32 -07:00
- ( void ) stageStarting : ( const char * ) stageName {
2015-02-10 21:26:55 -05:00
Log ( LOG_I , @ "Starting %s" , stageName ) ;
2014-10-21 17:18:30 -04:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
NSString * lowerCase = [ NSString stringWithFormat : @ "%s in progress..." , stageName ] ;
NSString * titleCase = [ [ [ lowerCase substringToIndex : 1 ] uppercaseString ] stringByAppendingString : [ lowerCase substringFromIndex : 1 ] ] ;
2020-11-01 20:00:39 -06:00
[ self -> _stageLabel setText : titleCase ] ;
[ self -> _stageLabel sizeToFit ] ;
self -> _stageLabel . center = CGPointMake ( self . view . frame . size . width / 2 , self -> _stageLabel . center . y ) ;
2014-10-21 17:18:30 -04:00
} ) ;
2014-10-21 14:54:10 -04:00
}
2016-07-31 17:10:32 -07:00
- ( void ) stageComplete : ( const char * ) stageName {
2014-10-21 14:54:10 -04:00
}
2020-08-10 22:56:47 -07:00
- ( void ) stageFailed : ( const char * ) stageName withError : ( int ) errorCode portTestFlags : ( int ) portTestFlags {
2020-02-25 00:00:00 -08:00
Log ( LOG_I , @ "Stage %s failed: %d" , stageName , errorCode ) ;
2020-08-10 22:56:47 -07:00
unsigned int portTestResults = LiTestClientConnectivity ( CONN_TEST _SERVER , 443 , portTestFlags ) ;
2015-06-23 21:46:58 -07:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2018-05-30 22:04:06 -07:00
// Allow the display to go to sleep now
[ UIApplication sharedApplication ] . idleTimerDisabled = NO ;
2020-08-10 22:56:47 -07:00
NSString * message = [ NSString stringWithFormat : @ "%s failed with error %d" , stageName , errorCode ] ;
2020-12-23 14:48:20 -06:00
if ( portTestFlags ! = 0 ) {
char failingPorts [ 256 ] ;
LiStringifyPortFlags ( portTestFlags , "\n" , failingPorts , sizeof ( failingPorts ) ) ;
message = [ message stringByAppendingString : [ NSString stringWithFormat : @ "\n\nCheck your firewall and port forwarding rules for port(s):\n%s" , failingPorts ] ] ;
}
2020-08-10 22:56:47 -07:00
if ( portTestResults ! = ML_TEST _RESULT _INCONCLUSIVE && portTestResults ! = 0 ) {
2020-08-30 15:37:52 -07:00
message = [ message stringByAppendingString : @ "\n\nYour device's network connection is blocking Moonlight. Streaming may not work while connected to this network." ] ;
2020-08-10 22:56:47 -07:00
}
2015-06-23 21:46:58 -07:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "Connection Failed"
2020-08-10 22:56:47 -07:00
message : message
2015-06-23 21:46:58 -07:00
preferredStyle : UIAlertControllerStyleAlert ] ;
2018-08-29 09:36:51 -07:00
[ Utils addHelpOptionToDialog : alert ] ;
2018-05-28 18:20:59 -07:00
[ alert addAction : [ UIAlertAction actionWithTitle : @ "OK" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2015-06-23 21:46:58 -07:00
[ self returnToMainFrame ] ;
} ] ] ;
[ self presentViewController : alert animated : YES completion : nil ] ;
} ) ;
2015-10-17 03:45:20 -07:00
2016-04-12 00:50:59 -04:00
[ _streamMan stopStream ] ;
2014-10-21 14:54:10 -04:00
}
2014-10-23 23:41:07 -04:00
- ( void ) launchFailed : ( NSString * ) message {
2015-06-23 21:46:58 -07:00
Log ( LOG_I , @ "Launch failed: %@" , message ) ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2018-05-30 22:04:06 -07:00
// Allow the display to go to sleep now
[ UIApplication sharedApplication ] . idleTimerDisabled = NO ;
2019-10-12 19:20:53 -07:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "Connection Error"
2015-06-23 21:46:58 -07:00
message : message
preferredStyle : UIAlertControllerStyleAlert ] ;
2018-08-29 09:36:51 -07:00
[ Utils addHelpOptionToDialog : alert ] ;
2018-05-28 18:20:59 -07:00
[ alert addAction : [ UIAlertAction actionWithTitle : @ "OK" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2015-06-23 21:46:58 -07:00
[ self returnToMainFrame ] ;
} ] ] ;
[ self presentViewController : alert animated : YES completion : nil ] ;
} ) ;
2014-10-21 14:54:10 -04:00
}
2019-02-11 19:22:18 -08:00
- ( void ) rumble : ( unsigned short ) controllerNumber lowFreqMotor : ( unsigned short ) lowFreqMotor highFreqMotor : ( unsigned short ) highFreqMotor {
Log ( LOG_I , @ "Rumble on gamepad %d: %04x %04x" , controllerNumber , lowFreqMotor , highFreqMotor ) ;
[ _controllerSupport rumble : controllerNumber lowFreqMotor : lowFreqMotor highFreqMotor : highFreqMotor ] ;
}
2019-07-23 18:43:41 -04:00
- ( void ) connectionStatusUpdate : ( int ) status {
Log ( LOG_W , @ "Connection status update: %d" , status ) ;
2020-11-01 13:27:10 -06:00
// The stats overlay takes precedence over these warnings
if ( _statsUpdateTimer ! = nil ) {
return ;
}
2019-07-23 18:43:41 -04:00
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
switch ( status ) {
case CONN_STATUS _OKAY :
[ self updateOverlayText : nil ] ;
break ;
case CONN_STATUS _POOR :
if ( self -> _streamConfig . bitRate > 5000 ) {
[ self updateOverlayText : @ "Slow connection to PC\nReduce your bitrate" ] ;
}
else {
[ self updateOverlayText : @ "Poor connection to PC" ] ;
}
break ;
}
} ) ;
}
2022-02-13 14:27:17 -06:00
- ( void ) updatePreferredDisplayMode : ( BOOL ) streamActive {
# if TARGET_OS _TV
if ( @ available ( tvOS 11.2 , * ) ) {
UIWindow * window = [ [ [ UIApplication sharedApplication ] delegate ] window ] ;
AVDisplayManager * displayManager = [ window avDisplayManager ] ;
// This logic comes from Kodi and MrMC
if ( streamActive ) {
int dynamicRange ;
if ( LiGetCurrentHostDisplayHdrMode ( ) ) {
dynamicRange = 2 ; // HDR10
}
else {
dynamicRange = 0 ; // SDR
}
AVDisplayCriteria * displayCriteria = [ [ AVDisplayCriteria alloc ] initWithRefreshRate : [ _settings . framerate floatValue ]
videoDynamicRange : dynamicRange ] ;
displayManager . preferredDisplayCriteria = displayCriteria ;
}
else {
// Switch back to the default display mode
displayManager . preferredDisplayCriteria = nil ;
}
}
# endif
}
- ( void ) setHdrMode : ( bool ) enabled {
Log ( LOG_I , @ "HDR is now: %s" , enabled ? "active" : "inactive" ) ;
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
[ self updatePreferredDisplayMode : YES ] ;
} ) ;
}
2020-11-01 20:00:39 -06:00
- ( void ) videoContentShown {
[ _spinner stopAnimating ] ;
2020-11-01 20:22:01 -06:00
[ self . view setBackgroundColor : [ UIColor blackColor ] ] ;
2020-11-01 20:00:39 -06:00
}
2014-08-31 13:13:46 -04:00
- ( void ) didReceiveMemoryWarning
{
[ super didReceiveMemoryWarning ] ;
// Dispose of any resources that can be recreated .
}
2019-10-22 00:32:56 -04:00
- ( void ) gamepadPresenceChanged {
# if ! TARGET_OS _TV
if ( @ available ( iOS 11.0 , * ) ) {
[ self setNeedsUpdateOfHomeIndicatorAutoHidden ] ;
}
# endif
}
2020-09-18 18:48:35 -05:00
- ( void ) mousePresenceChanged {
# if ! TARGET_OS _TV
if ( @ available ( iOS 14.0 , * ) ) {
[ self setNeedsUpdateOfPrefersPointerLocked ] ;
}
# endif
}
2019-11-02 15:17:52 -07:00
- ( void ) userInteractionBegan {
// Disable hiding home bar when user is interacting .
// iOS will force it to be shown anyway , but it will
// also discard our edges deferring system gestures unless
// we willingly give up home bar hiding preference .
_userIsInteracting = YES ;
# if ! TARGET_OS _TV
if ( @ available ( iOS 11.0 , * ) ) {
[ self setNeedsUpdateOfHomeIndicatorAutoHidden ] ;
}
# endif
}
- ( void ) userInteractionEnded {
// Enable home bar hiding again if conditions allow
_userIsInteracting = NO ;
# if ! TARGET_OS _TV
if ( @ available ( iOS 11.0 , * ) ) {
[ self setNeedsUpdateOfHomeIndicatorAutoHidden ] ;
}
# endif
}
2018-08-26 11:31:20 -07:00
# if ! TARGET_OS _TV
2017-09-15 00:30:51 -07:00
// Require a confirmation when streaming to activate a system gesture
- ( UIRectEdge ) preferredScreenEdgesDeferringSystemGestures {
return UIRectEdgeAll ;
}
2019-10-22 00:32:56 -04:00
- ( BOOL ) prefersHomeIndicatorAutoHidden {
if ( [ _controllerSupport getConnectedGamepadCount ] > 0 &&
2019-11-02 15:17:52 -07:00
[ _streamView getCurrentOscState ] = = OnScreenControlsLevelOff &&
_userIsInteracting = = NO ) {
2019-10-22 00:32:56 -04:00
// Autohide the home bar when a gamepad is connected
// and the on - screen controls are disabled . We can ' t
// do this all the time because any touch on the display
// will cause the home indicator to reappear , and our
// preferredScreenEdgesDeferringSystemGestures will also
// be suppressed ( leading to possible errant exits of the
// stream ) .
return YES ;
}
return NO ;
}
2014-10-20 21:39:40 -04:00
- ( BOOL ) shouldAutorotate {
return YES ;
}
2020-09-18 18:27:20 -05:00
- ( BOOL ) prefersPointerLocked {
2020-09-18 18:48:35 -05:00
// Pointer lock breaks the UIKit mouse APIs , which is a problem because
// GCMouse is horribly broken on iOS 14.0 for certain mice . Only lock
// the cursor if there is a GCMouse present .
return [ GCMouse mice ] . count > 0 ;
2020-09-18 18:27:20 -05:00
}
2018-08-26 11:31:20 -07:00
# endif
2014-08-31 13:13:46 -04:00
@ end