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"
2014-08-31 13:13:46 -04:00
# include < sys / socket . h >
# include < netinet / in . h >
# include < arpa / inet . h >
2014-10-20 21:13:13 -04:00
@ implementation StreamFrameViewController {
ControllerSupport * _controllerSupport ;
2014-10-21 01:54:31 -04:00
StreamManager * _streamMan ;
2018-06-09 16:29:48 -07:00
NSTimer * _inactivityTimer ;
2018-10-23 23:18:56 -04:00
UITapGestureRecognizer * _menuGestureRecognizer ;
UITapGestureRecognizer * _menuDoubleTapGestureRecognizer ;
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
}
2018-10-23 23:18:56 -04:00
# ifdef TARGET_OS _TV
- ( void ) controllerPauseButtonPressed : ( id ) sender { }
- ( void ) controllerPauseButtonDoublePressed : ( id ) sender {
Log ( LOG_I , @ "Menu double-pressed -- backing out of stream" ) ;
[ self returnToMainFrame ] ;
}
# 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 ] ;
2018-06-01 19:11:10 -07:00
[ self . stageLabel setText : [ NSString stringWithFormat : @ "Starting %@..." , self . streamConfig . appName ] ] ;
2014-10-26 02:15:53 -04:00
[ self . stageLabel sizeToFit ] ;
2018-06-01 19:11:10 -07:00
self . stageLabel . textAlignment = NSTextAlignmentCenter ;
2014-11-27 00:02:18 -05:00
self . stageLabel . center = CGPointMake ( self . view . frame . size . width / 2 , self . view . frame . size . height / 2 ) ;
2014-11-27 16:23:06 -05:00
self . spinner . center = CGPointMake ( self . view . frame . size . width / 2 , self . view . frame . size . height / 2 - self . stageLabel . frame . size . height - self . spinner . frame . size . height ) ;
2014-08-31 13:13:46 -04:00
[ UIApplication sharedApplication ] . idleTimerDisabled = YES ;
2014-10-19 00:09:47 -04:00
2018-06-02 13:52:58 -07:00
_controllerSupport = [ [ ControllerSupport alloc ] initWithConfig : self . streamConfig ] ;
2018-06-09 16:29:48 -07:00
_inactivityTimer = nil ;
2018-10-23 23:18:56 -04:00
# if TARGET_OS _TV
if ( ! _menuGestureRecognizer || ! _menuDoubleTapGestureRecognizer ) {
_menuGestureRecognizer = [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( controllerPauseButtonPressed : ) ] ;
_menuGestureRecognizer . allowedPressTypes = @ [ @ ( UIPressTypeMenu ) ] ;
_menuDoubleTapGestureRecognizer = [ [ UITapGestureRecognizer alloc ] initWithTarget : self action : @ selector ( controllerPauseButtonDoublePressed : ) ] ;
_menuDoubleTapGestureRecognizer . numberOfTapsRequired = 2 ;
[ _menuGestureRecognizer requireGestureRecognizerToFail : _menuDoubleTapGestureRecognizer ] ;
_menuDoubleTapGestureRecognizer . allowedPressTypes = @ [ @ ( UIPressTypeMenu ) ] ;
}
[ self . view addGestureRecognizer : _menuGestureRecognizer ] ;
[ self . view addGestureRecognizer : _menuDoubleTapGestureRecognizer ] ;
# endif
2018-12-22 21:43:37 -08:00
# if TARGET_OS _TV
[ self . tipLabel setText : @ "Tip: Double tap the Menu button to disconnect from your PC" ] ;
# else
[ self . tipLabel setText : @ "Tip: Swipe from the left edge to disconnect from your PC" ] ;
# endif
[ self . tipLabel sizeToFit ] ;
self . tipLabel . textAlignment = NSTextAlignmentCenter ;
2018-12-28 20:32:22 -08:00
self . 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
2014-10-21 14:54:10 -04:00
renderView : self . view
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 ] ;
}
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
}
2014-10-29 00:47:51 -04:00
- ( void ) returnToMainFrame {
[ 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 ] ;
}
// Terminate the stream if the app is inactive for 10 seconds
Log ( LOG_I , @ "Starting inactivity termination timer" ) ;
_inactivityTimer = [ NSTimer scheduledTimerWithTimeInterval : 10
target : self
selector : @ selector ( inactiveTimerExpired : )
userInfo : nil
repeats : NO ] ;
}
- ( 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 .
self . stageLabel . hidden = YES ;
2018-12-22 21:43:37 -08:00
self . tipLabel . hidden = YES ;
2018-08-26 11:31:20 -07:00
# if ! TARGET_OS _TV
2018-04-21 22:20:27 -07:00
[ ( StreamView * ) self . view setupOnScreenControls : self -> _controllerSupport swipeDelegate : self ] ;
2018-08-26 11:31:20 -07:00
# endif
2014-10-21 17:18:30 -04:00
} ) ;
2014-10-21 14:54:10 -04:00
}
- ( void ) connectionTerminated : ( long ) errorCode {
2015-02-10 21:26:55 -05:00
Log ( LOG_I , @ "Connection terminated: %ld" , errorCode ) ;
2014-10-21 14:54:10 -04: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 ;
2015-01-09 17:54:21 -05:00
UIAlertController * conTermAlert = [ UIAlertController alertControllerWithTitle : @ "Connection Terminated" message : @ "The connection was terminated" preferredStyle : UIAlertControllerStyleAlert ] ;
2018-05-28 18:20:59 -07:00
[ conTermAlert addAction : [ UIAlertAction actionWithTitle : @ "OK" style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2015-01-09 17:54:21 -05:00
[ self returnToMainFrame ] ;
} ] ] ;
[ self presentViewController : conTermAlert animated : YES completion : nil ] ;
} ) ;
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 ] ] ;
[ self . stageLabel setText : titleCase ] ;
2014-10-26 02:15:53 -04:00
[ 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
}
2016-07-31 17:10:32 -07:00
- ( void ) stageFailed : ( const char * ) stageName withError : ( long ) errorCode {
2015-06-23 21:46:58 -07:00
Log ( LOG_I , @ "Stage %s failed: %ld" , stageName , errorCode ) ;
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 ;
2015-06-23 21:46:58 -07:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "Connection Failed"
message : [ NSString stringWithFormat : @ "%s failed with error %ld" ,
stageName , errorCode ]
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 ;
2015-06-23 21:46:58 -07:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : @ "Connection Failed"
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
}
2016-07-31 17:10:32 -07:00
- ( void ) displayMessage : ( const char * ) message {
2015-02-10 21:26:55 -05:00
Log ( LOG_I , @ "Display message: %s" , message ) ;
2014-10-21 14:54:10 -04:00
}
2016-07-31 17:10:32 -07:00
- ( void ) displayTransientMessage : ( const char * ) message {
2015-02-10 21:26:55 -05:00
Log ( LOG_I , @ "Display transient message: %s" , message ) ;
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 ] ;
}
2014-08-31 13:13:46 -04:00
- ( void ) didReceiveMemoryWarning
{
[ super didReceiveMemoryWarning ] ;
// Dispose of any resources that can be recreated .
}
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 ;
}
2014-10-20 21:39:40 -04:00
- ( BOOL ) shouldAutorotate {
return YES ;
}
2018-08-26 11:31:20 -07:00
# endif
2014-08-31 13:13:46 -04:00
@ end