Compare commits

..
78 Commits
Author SHA1 Message Date
Diego Waxemberg 15fe5a06c2 Bumping version for v3.0.0 build 3 2019-09-22 22:14:27 -07:00
Cameron Gutman 8d4c7bd705 Fix Steam app detection if GFE gives us a lower-case path 2019-09-22 10:31:23 -07:00
Cameron Gutman b0d85abfa6 Increase audio circular buffer size to reduce glitching 2019-09-22 10:20:30 -07:00
Diego Waxemberg 4295017324 Bumping version for v3.0.0 build 2
Pulling in fix for assets
2019-09-21 23:26:09 -07:00
Cameron Gutman d80d70215a Add App Store screenshots 2019-09-21 16:45:45 -07:00
Cameron Gutman a46f905aa6 Fix case of Computer asset name 2019-09-21 14:32:12 -07:00
Diego Waxemberg 453c50d505 Bumping version for v3.0.0 2019-09-20 20:26:09 -07:00
Cameron Gutman 61c7d9f2b3 Disable bounce-back on the reveal view controller 2019-09-18 19:59:24 -07:00
Cameron Gutman 34707e7743 Update launch screen to match new UI 2019-09-18 19:45:27 -07:00
Cameron Gutman 9a9b9651a1 Address several iOS and tvOS compilation warnings 2019-09-15 15:46:23 -07:00
Cameron Gutman d4576a8739 Suppress availability warning for overrideUserInterfaceStyle field 2019-09-15 15:29:29 -07:00
Cameron Gutman 87824c2650 Fix control stream connection error on multi-homed hosts 2019-09-14 14:14:26 -07:00
Cameron Gutman 283e735f90 Remove the successful quit dialog since it's redundant and annoying 2019-09-14 13:08:47 -07:00
Cameron Gutman 202f968b98 Rework app labels on iOS to reuse title UILabel and use correct size and insets 2019-09-14 12:59:40 -07:00
Cameron Gutman 62df1cd5ba Reduce minimum spacing to 15 to allow an extra column on iPhone SE 2019-09-14 12:47:21 -07:00
Cameron Gutman f24c28c848 Switch back to text for navigation buttons 2019-09-14 12:46:50 -07:00
Cameron Gutman 8fb465d3ca Fix tvOS build warning 2019-09-14 12:28:37 -07:00
Cameron Gutman 29dff2320b Fix content insets for host and app scroll views 2019-09-14 12:28:21 -07:00
Cameron Gutman a40b30016b Rewrite storyboards with Xcode 11 GM 2019-09-14 12:07:38 -07:00
Cameron Gutman 409b262a4f Add a hack as a possible workaround for #372 2019-09-14 11:49:09 -07:00
Cameron Gutman 73197e4378 Clean up and fix storyboards 2019-09-14 11:34:22 -07:00
Cameron Gutman f51dbe4295 Fix some Xcode 11 warnings 2019-09-11 19:12:11 -07:00
Cameron Gutman 597aaa1bab Cleanup controller support code using the iOS 13 SDK directly 2019-09-11 18:44:37 -07:00
Cameron Gutman b9656da8a5 Use Xcode 11 image for Travis CI 2019-09-11 18:27:45 -07:00
Cameron Gutman 422db1898c Improve manually added Steam app detection heuristic to prevent FPs on apps with "Steam" in the name 2019-09-08 18:56:18 -07:00
Cameron Gutman 56691791b8 Fix race condition that could cause safe area insets to be missing 2019-09-01 12:26:39 -07:00
Cameron Gutman de2a5d3cd7 Fix overlays not being affected by button press and spinner eating touch events 2019-09-01 12:15:26 -07:00
Cameron Gutman 49fc10191c Fix transparent app grid background being incorrectly overridden 2019-08-31 20:19:03 -07:00
Cameron Gutman bf8aaa8198 Refresh app icon when the running state changes on tvOS 2019-08-31 19:52:55 -07:00
Cameron Gutman 9a4cdc9c94 Replace updating overlay icon with spinner 2019-08-31 16:02:37 -07:00
Cameron Gutman f6153fe818 Improve selection visibility on tvOS 2019-08-30 22:33:32 -07:00
Cameron Gutman f2fa9e18ea Reorder tvOS settings and fix default bitrate for 1080p60 2019-08-30 21:51:10 -07:00
Cameron Gutman aa013be6e6 Update settings config for tvOS 2019-08-30 21:44:12 -07:00
Cameron Gutman 7e21b131ac Redesign navigation bar on iOS 2019-08-30 21:15:11 -07:00
Cameron Gutman 97681f3a0d Increase size of PC icon 2019-08-30 20:00:00 -07:00
Cameron Gutman e476cc407c Fix crash on pre-iOS 13 2019-08-30 19:55:49 -07:00
Cameron Gutman f2e03bbf1f Always use dark mode on tvOS 2019-08-30 19:40:17 -07:00
Cameron Gutman a6cef32c9a Increase size of PC icons on tvOS 2019-08-30 19:37:16 -07:00
Cameron Gutman 6d94897c21 Distinguish between offline and unknown hosts 2019-08-30 19:19:23 -07:00
Cameron Gutman a065c78b5f Restyle UIComputerView to match other clients 2019-08-30 18:36:14 -07:00
Cameron Gutman 83479719a4 Fix styles on settings slideout on iOS 13 2019-08-30 17:16:56 -07:00
Cameron Gutman fd4ede58ca Reduce the size of the audio circular buffer to reduce peak latency 2019-08-29 22:00:27 -07:00
Cameron Gutman 7ed5a9831f Fix settings slideout scrolling too far on iOS 13 2019-08-29 21:57:51 -07:00
Cameron Gutman 42b7fd35ce Fix surround sound audio playing back silence 2019-08-29 21:07:10 -07:00
Cameron Gutman 42c97eafaa Fix slightly offset titles 2019-08-29 20:34:51 -07:00
Cameron Gutman 5b82be0546 Rewrite UIAppView rendering to consolidate tvOS and iOS codepaths 2019-08-29 20:32:18 -07:00
Cameron Gutman 1529058f52 Increase font size for app names 2019-08-29 19:00:42 -07:00
Cameron Gutman 3dcca3a922 Allow click and drag via tvOS remote trackpad 2019-08-29 18:55:33 -07:00
Cameron Gutman 9b8f230c46 Prevent gesture recognizers from eating our touch events. Fixes #375 2019-08-28 21:51:52 -07:00
Cameron Gutman 7699d9e574 Implement 5.1 surround sound support. Fixes #371 2019-08-26 22:46:35 -07:00
Cameron Gutman 641e1c5066 Update common-c for parallel STUN requests 2019-08-20 19:08:25 -07:00
Cameron Gutman 617ef7c6a6 Update common-c to support STUN fallback 2019-08-07 23:15:33 -07:00
Cameron Gutman c2620b8321 Fix tvOS build 2019-08-07 00:53:58 -07:00
Cameron Gutman 9e91d136bf Add Travis CI badge to readme 2019-08-07 00:48:26 -07:00
Cameron Gutman 9960d2a2ff Add Travis CI support 2019-08-07 00:40:54 -07:00
Cameron Gutman 311e77e01a Remove unit test for the old Swift controller class 2019-08-07 00:06:54 -07:00
Cameron Gutman c08df921da Update common-c to fix termination detection issue on GFE 3.19.0.107 2019-07-28 12:58:25 -04:00
Cameron Gutman ba4e55aac4 Add on-screen connection status warning 2019-07-23 18:43:41 -04:00
Cameron Gutman af16f03703 Stringify IPv6 addresses with scope IDs if present 2019-07-15 01:05:21 -07:00
Cameron Gutman 8189d54703 Remove forced population of host.address 2019-07-15 01:00:50 -07:00
Cameron Gutman ac8514e3cb Update common-c for IPv6 optimizations 2019-07-15 00:16:54 -07:00
Cameron Gutman 8f4b8da3ce Handle IPv6-only servers 2019-07-14 22:55:41 -07:00
Cameron Gutman 8337b3e708 Add support for zero configuration IPv6 streaming 2019-07-14 19:17:17 -07:00
Cameron Gutman cb527baead Generate SHA-256 client certificates instead of SHA-1 2019-07-05 22:20:55 -07:00
Cameron Gutman ed10612e2c Allow pairing to complete in the background to allow users to type the PIN using remote desktop apps 2019-07-05 22:18:30 -07:00
Diego Waxemberg 7d3eaeef83 Creating a new 2.7.0 build 2019-06-05 20:22:31 -07:00
Cameron Gutman dd5ecb37c4 Add support for Start and Select on iOS 13. Fixes #367 2019-06-05 19:54:56 -07:00
Cameron Gutman 636143ee97 Disable the 4K option for SoCs older than the A9 2019-06-05 18:59:40 -07:00
Cameron Gutman 28ff29bb96 Disable the HEVC toggle for devices that don't support HEVC 2019-06-05 18:41:30 -07:00
Cameron Gutman a508c6d855 Remove default Steam app to address Apple App Review rejection 2019-06-04 18:37:52 -07:00
Diego Waxemberg dbd6eabd24 Bumping app version for v2.7.0 2019-05-31 18:54:02 -07:00
Cameron Gutman ad0a5935f7 Don't compile asserts into release builds 2019-05-31 14:46:20 -05:00
Cameron Gutman dcd2dc97a6 Merge pull request #366 from MichaelMKenny/4K-120-bitrate-fix
Capped bitrate at 100Mbps.
2019-05-31 14:04:10 -05:00
Michael Kenny f92544c7d2 Capped bitrate at 100Mbps.
Choosing 4K 120Hz was causing an assert/crash.
2019-05-31 15:04:20 +09:30
Cameron Gutman 797fa0f492 Add 4K resolution option. Fixes #364 2019-05-27 10:37:26 -07:00
Cameron Gutman ff52adf4f8 Improve pairing messages 2019-05-27 10:27:10 -07:00
Cameron Gutman 8bb49ada11 Disable vibration for expedited review 2019-05-27 10:12:02 -07:00
Cameron Gutman 3f709b616b Fix video on GFE 3.19 2019-05-27 10:06:57 -07:00
77 changed files with 1530 additions and 1011 deletions
+25
View File
@@ -0,0 +1,25 @@
language: objective-c
git:
depth: 1
matrix:
include:
- os: osx
osx_image: xcode11
env:
- SCHEME="Moonlight"
- TARGET="platform=iOS Simulator,OS=12.1,name=iPhone SE"
- os: osx
osx_image: xcode11
env:
- SCHEME="Moonlight"
- TARGET="platform=iOS Simulator,OS=12.1,name=iPad Pro (9.7-inch)"
- os: osx
osx_image: xcode11
env:
- SCHEME="Moonlight TV"
- TARGET="platform=tvOS Simulator,OS=12.1,name=Apple TV 4K"
script:
- set -o pipefail && xcodebuild -project Moonlight.xcodeproj -scheme "$SCHEME" -destination "$TARGET" build | xcpretty
+11 -27
View File
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="8191" systemVersion="15A279b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina6_1" orientation="landscape" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<navigationController id="vV9-32-ww6">
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="y2P-aE-Haa">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
<color key="barTintColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="barTintColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</navigationBar>
<viewControllers>
<viewController id="l6V-ue-5Om">
@@ -22,30 +22,14 @@
<viewControllerLayoutGuide type="bottom" id="gBD-cy-cth"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="6lF-fc-kpK">
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
<navigationItem key="navigationItem" id="rW0-BH-s7l">
<barButtonItem key="leftBarButtonItem" enabled="NO" id="d9R-Zo-Uml">
<button key="customView" opaque="NO" contentMode="center" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="9JC-B5-BAE">
<rect key="frame" x="20" y="-1" width="109" height="46"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<state key="normal" title="Moonlight" image="Logo">
<color key="titleColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<color key="titleShadowColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
</state>
<variation key="widthClass=regular" preservesSuperviewLayoutMargins="NO"/>
</button>
</barButtonItem>
</navigationItem>
<navigationItem key="navigationItem" id="rW0-BH-s7l"/>
</viewController>
</viewControllers>
<point key="canvasLocation" x="34" y="149"/>
</navigationController>
</objects>
<resources>
<image name="Logo" width="32" height="32"/>
</resources>
</document>
+1 -33
View File
@@ -107,13 +107,7 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
*/
X509_set_issuer_name(x, name);
/* Add various extensions: standard extensions */
add_ext(x, NID_basic_constraints, "critical,CA:TRUE");
add_ext(x, NID_key_usage, "critical,keyCertSign,cRLSign");
add_ext(x, NID_subject_key_identifier, "hash");
if (!X509_sign(x, pk, EVP_sha1())) {
if (!X509_sign(x, pk, EVP_sha256())) {
goto err;
}
@@ -124,29 +118,3 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
err:
return(0);
}
/* Add extension using V3 code: we can set the config file as NULL
* because we wont reference any other sections.
*/
int add_ext(X509 *cert, int nid, char *value)
{
X509_EXTENSION *ex;
X509V3_CTX ctx;
/* This sets the 'context' of the extensions. */
/* No configuration database */
X509V3_set_ctx_nodb(&ctx);
/* Issuer and subject certs: both the target since it is self signed,
* no request and no CRL
*/
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ex) {
return 0;
}
X509_add_ext(cert, ex, -1);
X509_EXTENSION_free(ex);
return 1;
}
+1
View File
@@ -13,6 +13,7 @@
@property (nullable, nonatomic, retain) NSString *id;
@property (nullable, nonatomic, retain) NSString *name;
@property (nullable, nonatomic, retain) NSString *installPath;
@property (nonatomic) BOOL hdrSupported;
@property (nullable, nonatomic, retain) TemporaryHost *host;
+2 -1
View File
@@ -11,7 +11,7 @@
@interface TemporaryHost : NSObject
@property (nonatomic) BOOL online;
@property (nonatomic) State state;
@property (nonatomic) PairState pairState;
@property (nonatomic, nullable) NSString * activeAddress;
@property (nonatomic, nullable) NSString * currentGame;
@@ -20,6 +20,7 @@
@property (nonatomic, nullable, retain) NSString *address;
@property (nonatomic, nullable, retain) NSString *externalAddress;
@property (nonatomic, nullable, retain) NSString *localAddress;
@property (nonatomic, nullable, retain) NSString *ipv6Address;
@property (nonatomic, nullable, retain) NSString *mac;
@property (nonatomic) int serverCodecModeSupport;
+5
View File
@@ -16,6 +16,7 @@
self = [super init];
self.appList = [[NSMutableSet alloc] init];
self.currentGame = @"0";
self.state = StateUnknown;
return self;
}
@@ -26,6 +27,7 @@
self.address = host.address;
self.externalAddress = host.externalAddress;
self.localAddress = host.localAddress;
self.ipv6Address = host.ipv6Address;
self.mac = host.mac;
self.name = host.name;
self.uuid = host.uuid;
@@ -60,6 +62,9 @@
if (self.localAddress != nil) {
parentHost.localAddress = self.localAddress;
}
if (self.ipv6Address != nil) {
parentHost.ipv6Address = self.ipv6Address;
}
if (self.mac != nil) {
parentHost.mac = self.mac;
}
+1 -1
View File
@@ -54,7 +54,7 @@
self.width = [NSNumber numberWithInteger:1920];
break;
}
self.onscreenControls = (NSInteger)OnScreenControlsLevelOff;
self.onscreenControls = [NSNumber numberWithInteger:OnScreenControlsLevelOff];
#else
self.bitrate = settings.bitrate;
self.framerate = settings.framerate;
@@ -1,23 +0,0 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "limelight_computer_add_icon_1x.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "limelight_computer_add_icon_2x.png"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "limelight_computer_add_icon_3x.png"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

@@ -2,11 +2,11 @@
"images" : [
{
"idiom" : "universal",
"filename" : "add.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "logo.png",
"scale" : "2x"
},
{
Binary file not shown.
@@ -0,0 +1,69 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœ]‘]! „ß{Š^ÀÊ”Àâ1<‚Ùøó êý …E␍ »ÛN‡Yp°µƒmª‰×=)´³×•÷§À×7iØ·¸Hbä"‹= ¯3_\W«F­Æ!K©%­ðÁ½ÛñÎX¤@;$Ú®‹ ”mÊê@Œ¬Ç¿ ‰µµ+¸“!ïd�±ùË}œ#¡TÙ;7_Û«†,ÚLÙ˜EˆMHS~»ì8Ðà—î�$ëô¾±ÍX™F—ØÙ´<òŸ\W¾Í¤MlæÜûþeÓeÇÿÎ é¦Lg7
endstream
endobj
5 0 obj
212
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 0.3 /ca 0.3 >>
/a1 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 225 225 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830180928-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000648 00000 n
0000000430 00000 n
0000000326 00000 n
0000000015 00000 n
0000000304 00000 n
0000000713 00000 n
0000000829 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
881
%%EOF
+4 -6
View File
@@ -2,18 +2,16 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "limelight_computer_1x.png"
"filename" : "Computer.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "limelight_computer_2x.png"
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x",
"filename" : "limelight_computer_3x.png"
"scale" : "3x"
}
],
"info" : {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

@@ -2,17 +2,15 @@
"images" : [
{
"idiom" : "universal",
"filename" : "pull-arrow-light-14x31@1x.png",
"filename" : "error.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "pull-arrow-light-28x61@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "pull-arrow-light-42x92@3x.png",
"scale" : "3x"
}
],
@@ -0,0 +1,68 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœeŒÑ Ã0 Dÿ5Å-Uªì(kt„Rhò¤Ýª8µ �t︓Bb�å¯B)öù̸=ó—Œõž1fã¸òä�W˜sW½P’jçjXbó†kG Ý’rî%c?õì=ÓãÍ?ÜðÒ~Óƒ~®L+·
endstream
endobj
5 0 obj
115
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830180932-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000517 00000 n
0000000301 00000 n
0000000229 00000 n
0000000015 00000 n
0000000207 00000 n
0000000582 00000 n
0000000698 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
750
%%EOF
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "lock.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,68 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœUQKnÅ Üû¾@]ÌÇðŽñŽP!õu‘,ÚÞ_*ø¥"‚ÐÌØcZë�×Öξ�q¯Ÿ¾$|ýBÊ␍×wb+Ć�À<¨ßiÒ£ŠvW:9œ˜�O™•¸*˜ZΠšfwΰ çåœ;␍Ά ïZ§ÙÛá¸Ñ£ ×¢¨›y‚xœõ)Ñσd §r³�ËzÚa™Ç2ºUãòõp�oâè(|T'–JIrpóiI+4²Æ8´6§…êªÀd€5‘ÄxÏ␍v•Η¢_:'áiÔò tíöËïºk'¶vÎèP{ŽÙûЂ®¼$X2q|h“ê†Wv¦Q«?¹¹«‚ù_ùŸý Oøp…&
endstream
endobj
5 0 obj
267
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830180930-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000669 00000 n
0000000453 00000 n
0000000381 00000 n
0000000015 00000 n
0000000359 00000 n
0000000734 00000 n
0000000850 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
902
%%EOF
Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "settings.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -0,0 +1,70 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœeTIŽÜ0 ¼óú@‰Z,=#O ÌÌA}Hò ,.== °»d®Å¢j*òûVåq�t?èÕ„ßï÷ôýgIï¨]y¤V3�ôH­ç2Ù¡?¿ÁøvÐKÞm¹¥Þòåö÷ ƒþ|=òp,EØÏ„ö÷N4vnbjÏGª#OxRå\ÇL¯68Z+€™ß‰ �p´÷ä¼*G”iÆî3ÃÕ}ÓQ¯Þã ^Ê‘ûÜÿÄ<O¬ER„úR\¸Jï-ï2�‘G
´2×␍OA nŸ;J^CXFR¾V»!PïJ” 8 šÂ�øv8␍Ö,G³Î␍<Q°À.¾×¥VÛ�|ÄX¯Üa\2zí37)qT+fä‚䌗iGJ‚«¦ílyVîÒÏÖ K�iZã¦Wû¦ˆ Î–+¿L·±5jX�~µP‚J«šH�)»~6xïè␍^¼´@i°ŒÎE_wbަ.e‹«%V^PsÙg.ÙâêŒê´^ºrw€J0Ê„—°$Ã!@’ ti^[•­µiZØÅ€¬»‡PADt?áTúÃqê"zÔáô¤B�횕Ԕ Šz[±²naä·÷*ô Ë "·ÊÁ+Ã$úÇ;Hæ®*ò°2�á@qlÂóÙÁ³w‚␍WªÒωѣV9vYø2ºhžW„«Ê0™1s7pAz(—+©\ÎÞÃ�ÚU=êD±
l èk"␍š}�Ę•-_²—m }%ßO0ë›®½Ïë»-X·Ëö¾Ùú­€yÖ␍¶üÖZÛy^)&†¸nL(¯WÑÿWÓý ¿ ¾?1
endstream
endobj
5 0 obj
608
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830202122-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000001010 00000 n
0000000794 00000 n
0000000722 00000 n
0000000015 00000 n
0000000700 00000 n
0000001075 00000 n
0000001191 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
1243
%%EOF
+21
View File
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "up_arrow.pdf",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
+70
View File
@@ -0,0 +1,70 @@
%PDF-1.5
%µí®û
4 0 obj
<< /Length 5 0 R
/Filter /FlateDecode
>>
stream
xœUŽQ
ƒ@ Dÿsй€1ÙÍF=†G(…êÇú¡½?-e`ƒÌ$
5Ö¼ZIqh›Ð¾Ó—4qAî¨ó ÓsTdã¹dl>üØ�H… {º‰n|–KÏâé,Ë}°?‡ê5ôª™ÿ^øÐH;U$}
endstream
endobj
5 0 obj
114
endobj
3 0 obj
<<
/ExtGState <<
/a0 << /CA 1 /ca 1 >>
>>
>>
endobj
2 0 obj
<< /Type /Page % 1
/Parent 1 0 R
/MediaBox [ 0 0 75 75 ]
/Contents 4 0 R
/Group <<
/Type /Group
/S /Transparency
/I true
/CS /DeviceRGB
>>
/Resources 3 0 R
>>
endobj
1 0 obj
<< /Type /Pages
/Kids [ 2 0 R ]
/Count 1
>>
endobj
6 0 obj
<< /Producer (cairo 1.16.0 (https://cairographics.org))
/CreationDate (D:20190830203401-07'00)
>>
endobj
7 0 obj
<< /Type /Catalog
/Pages 1 0 R
>>
endobj
xref
0 8
0000000000 65535 f
0000000516 00000 n
0000000300 00000 n
0000000228 00000 n
0000000015 00000 n
0000000206 00000 n
0000000581 00000 n
0000000697 00000 n
trailer
<< /Size 8
/Root 7 0 R
/Info 6 0 R
>>
startxref
749
%%EOF
+44 -14
View File
@@ -46,6 +46,7 @@
-(void) rumbleController: (Controller*)controller
{
#if 0
// Only vibrate if the amplitude is large enough
if (controller.lowFreqMotor > 0x5000 || controller.highFreqMotor > 0x5000) {
// If the gamepad is nil (on-screen controls) or it's attached to the device,
@@ -54,6 +55,7 @@
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
}
}
#endif
}
-(void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor
@@ -192,6 +194,9 @@
[_controllerStreamLock unlock];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
-(void) unregisterControllerCallbacks:(GCController*) controller
{
if (controller != NULL) {
@@ -209,21 +214,28 @@
-(void) registerControllerCallbacks:(GCController*) controller
{
if (controller != NULL) {
controller.controllerPausedHandler = ^(GCController *controller) {
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
// Get off the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self setButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
// On iOS 13, we want to use the new buttonMenu property which lets users hold down Start.
// On prior versions, we must use the controllerPausedHandler.
if (@available(iOS 13.0, tvOS 13.0, *)) {
// Nothing - buttonMenu is already handled in valueChangedHandler
}
else {
controller.controllerPausedHandler = ^(GCController *controller) {
Controller* limeController = [self->_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
// Pause for 100 ms
usleep(100 * 1000);
[self clearButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
});
};
// Get off the main thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self setButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
// Pause for 100 ms
usleep(100 * 1000);
[self clearButtonFlag:limeController flags:PLAY_FLAG];
[self updateFinished:limeController];
});
};
}
if (controller.extendedGamepad != NULL) {
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
@@ -255,6 +267,16 @@
}
}
if (@available(iOS 13.0, tvOS 13.0, *)) {
// Menu button is mandatory, so no need to check for nil
UPDATE_BUTTON_FLAG(limeController, PLAY_FLAG, gamepad.buttonMenu.pressed);
// Options button is optional (only present on Xbox One S and PS4 gamepads)
if (gamepad.buttonOptions != nil) {
UPDATE_BUTTON_FLAG(limeController, BACK_FLAG, gamepad.buttonOptions.pressed);
}
}
leftStickX = gamepad.leftThumbstick.xAxis.value * 0x7FFE;
leftStickY = gamepad.leftThumbstick.yAxis.value * 0x7FFE;
@@ -315,6 +337,12 @@
if (controller.extendedGamepad.leftThumbstickButton != nil &&
controller.extendedGamepad.rightThumbstickButton != nil) {
level = OnScreenControlsLevelAutoGCExtendedGamepadWithStickButtons;
if (@available(iOS 13.0, tvOS 13.0, *)) {
if (controller.extendedGamepad.buttonOptions != nil) {
// Has L3/R3 and Select, so we can show nothing :)
level = OnScreenControlsLevelOff;
}
}
}
}
break;
@@ -403,6 +431,8 @@
return controller.extendedGamepad != nil || controller.gamepad != nil;
}
#pragma clang diagnostic pop
+(int) getGamepadCount {
int count = 0;
+1 -1
View File
@@ -120,7 +120,7 @@ static float L3_Y;
_edgeDelegate = swipeDelegate;
_deadTouches = [[NSMutableArray alloc] init];
_iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
_iPad = ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad);
_controlArea = CGRectMake(0, 0, _view.frame.size.width, _view.frame.size.height);
if (_iPad)
{
+1
View File
@@ -18,6 +18,7 @@
@property (nonatomic, retain) IBOutlet UITextField* keyInputField;
- (void) setupStreamView;
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate;
- (void) setMouseDeltaFactors:(float)x y:(float)y;
+49
View File
@@ -26,6 +26,11 @@
float yDeltaFactor;
float screenFactor;
#if TARGET_OS_TV
UIGestureRecognizer* remotePressRecognizer;
UIGestureRecognizer* remoteLongPressRecognizer;
#endif
NSDictionary<NSString *, NSNumber *> *dictCodes;
}
@@ -36,6 +41,19 @@
screenFactor = [[UIScreen mainScreen] scale];
}
- (void) setupStreamView {
#if TARGET_OS_TV
remotePressRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(remoteButtonPressed:)];
remotePressRecognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
remoteLongPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(remoteButtonLongPressed:)];
remoteLongPressRecognizer.allowedPressTypes = @[@(UIPressTypeSelect)];
[self addGestureRecognizer:remotePressRecognizer];
[self addGestureRecognizer:remoteLongPressRecognizer];
#endif
}
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate {
onScreenControls = [[OnScreenControls alloc] initWithView:self controllerSup:controllerSupport swipeDelegate:swipeDelegate];
DataManager* dataMan = [[DataManager alloc] init];
@@ -199,6 +217,37 @@
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
}
#if TARGET_OS_TV
- (void)remoteButtonPressed:(id)sender {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
Log(LOG_D, @"Sending left mouse button press");
// Mark this as touchMoved to avoid a duplicate press on touch up
self->touchMoved = true;
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
// Wait 100 ms to simulate a real button press
usleep(100 * 1000);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
});
}
- (void)remoteButtonLongPressed:(id)sender {
Log(LOG_D, @"Holding left mouse button");
isDragging = true;
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
}
#endif
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// Disable all gesture recognizers to prevent them from eating our touches.
// This can happen on iOS 13 where the 3 finger tap gesture is taken over for
// displaying custom edit controls.
return NO;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
// This method is called when the "Return" key is pressed.
LiSendKeyboardEvent(0x0d, KEY_ACTION_DOWN, 0);
+2 -2
View File
@@ -19,11 +19,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.6.0</string>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>GCSupportedGameControllers</key>
<array>
<dict>
@@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Moonlight v1.3.xcdatamodel</string>
<string>Moonlight v1.4.xcdatamodel</string>
</dict>
</plist>
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14490.99" systemVersion="18F132" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="id" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
</entity>
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="ipv6Address" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="name" attributeType="String" syncable="YES"/>
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="serverCert" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
</entity>
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
</entity>
<elements>
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
<element name="Host" positionX="0" positionY="0" width="128" height="210"/>
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
</elements>
</model>
+44
View File
@@ -20,6 +20,7 @@ static const char* TAG_APP = "App";
static const char* TAG_APP_TITLE = "AppTitle";
static const char* TAG_APP_ID = "ID";
static const char* TAG_HDR_SUPPORTED = "IsHdrSupported";
static const char* TAG_APP_INSTALL_PATH = "AppInstallPath";
- (void)populateWithData:(NSData *)xml {
self.data = xml;
@@ -68,6 +69,7 @@ static const char* TAG_HDR_SUPPORTED = "IsHdrSupported";
NSString* appName = @"";
NSString* appId = nil;
NSString* hdrSupported = @"0";
NSString* appInstallPath = nil;
while (appInfoNode != NULL) {
if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_TITLE)) {
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
@@ -87,7 +89,14 @@ static const char* TAG_HDR_SUPPORTED = "IsHdrSupported";
hdrSupported = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
xmlFree(nodeVal);
}
} else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_INSTALL_PATH)) {
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
if (nodeVal != NULL) {
appInstallPath = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
xmlFree(nodeVal);
}
}
appInfoNode = appInfoNode->next;
}
if (appId != nil) {
@@ -95,6 +104,7 @@ static const char* TAG_HDR_SUPPORTED = "IsHdrSupported";
app.name = appName;
app.id = appId;
app.hdrSupported = [hdrSupported intValue] != 0;
app.installPath = appInstallPath;
[_appList addObject:app];
}
}
@@ -102,6 +112,40 @@ static const char* TAG_HDR_SUPPORTED = "IsHdrSupported";
}
xmlFreeDoc(docPtr);
// APP STORE REVIEW COMPLIANCE
//
// Remove default Steam entry from the app list to comply with Apple App Store Guideline 4.2.7d:
//
// The UI appearing on the client does not resemble an iOS or App Store view, does not provide a store-like interface,
// or include the ability to browse, select, or purchase software not already owned or licensed by the user.
//
// However, if the user manually adds Steam themselves, then we will display it.
TemporaryApp* officialSteamApp = nil;
TemporaryApp* manuallyAddedSteamApp = nil;
for (TemporaryApp* app in _appList) {
if (app.installPath != nil && [[app.installPath lowercaseString] hasSuffix:@"\\steam\\"]) {
// The official Steam app is marked as HDR supported, while manually added ones are not.
if ([app.name isEqualToString:@"Steam"] && app.hdrSupported) {
officialSteamApp = app;
}
else {
manuallyAddedSteamApp = app;
}
}
}
// To be safe, don't do anything if we didn't find an HDR-enabled Steam app.
if (officialSteamApp != nil) {
// If we didn't find a manually added Steam app, remove the official one to
// comply with the App Store guidelines.
if (manuallyAddedSteamApp == nil) {
[_appList removeObject:officialSteamApp];
}
else {
[_appList removeObject:manuallyAddedSteamApp];
}
}
}
- (NSSet*) getAppList {
+5 -12
View File
@@ -56,7 +56,7 @@
if ([serverInfoResponse isStatusOk]) {
host = [[TemporaryHost alloc] init];
host.activeAddress = host.address = hostAddress;
host.online = YES;
host.state = StateOnline;
[serverInfoResponse populateHost:host];
if (![self addHostToDiscovery:host]) {
callback(nil, @"Host information updated");
@@ -130,24 +130,17 @@
if (host.localAddress != nil) {
existingHost.localAddress = host.localAddress;
}
if (host.ipv6Address != nil) {
existingHost.ipv6Address = host.ipv6Address;
}
if (host.externalAddress != nil) {
existingHost.externalAddress = host.externalAddress;
}
existingHost.activeAddress = host.activeAddress;
existingHost.online = host.online;
existingHost.state = host.state;
return NO;
}
else {
// If we were added without an explicit address,
// populate it from our other available addresses
if (host.address == nil) {
if (host.externalAddress != nil) {
host.address = host.externalAddress;
}
else {
host.address = host.localAddress;
}
}
[_hostQueue addObject:host];
if (shouldDiscover) {
[_opQueue addOperation:[self createWorkerForHost:host]];
+5 -2
View File
@@ -52,6 +52,9 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
if (_host.externalAddress != nil) {
[array addObject:_host.externalAddress];
}
if (_host.ipv6Address != nil) {
[array addObject:_host.ipv6Address];
}
// Remove duplicate addresses from the list.
// This is done using an array rather than a set
@@ -115,9 +118,9 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
}
}
_host.online = receivedResponse;
_host.state = receivedResponse ? StateOnline : StateOffline;
if (receivedResponse) {
Log(LOG_D, @"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.uuid, _host.mac, _host.pairState, _host.online, _host.activeAddress);
Log(LOG_D, @"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t ipv6Address:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.ipv6Address, _host.uuid, _host.mac, _host.pairState, _host.state, _host.activeAddress);
}
}
+146 -14
View File
@@ -63,26 +63,158 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
[mDNSBrowser stop];
}
+ (NSString*)sockAddrToString:(NSData*)addrData {
char addrStr[INET6_ADDRSTRLEN];
struct sockaddr* addr = (struct sockaddr*)[addrData bytes];
if (addr->sa_family == AF_INET) {
inet_ntop(addr->sa_family, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
}
else {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr;
inet_ntop(addr->sa_family, &sin6->sin6_addr, addrStr, sizeof(addrStr));
if (sin6->sin6_scope_id != 0) {
// Link-local addresses with scope IDs are special
return [NSString stringWithFormat: @"%s%%%u", addrStr, sin6->sin6_scope_id];
}
}
return [NSString stringWithFormat: @"%s", addrStr];
}
+ (BOOL)isAddress:(uint8_t*)address inSubnet:(uint8_t*)subnet netmask:(int)bits {
for (int i = 0; i < bits; i++) {
uint8_t mask = 1 << (i % 8);
if ((address[i / 8] & mask) != (subnet[i / 8] & mask)) {
return NO;
}
}
return YES;
}
+ (BOOL)isLocalIpv6Address:(NSData*)addrData {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)[addrData bytes];
if (sin6->sin6_family != AF_INET6) {
return NO;
}
uint8_t* addrBytes = sin6->sin6_addr.s6_addr;
uint8_t prefix[2];
// fe80::/10
prefix[0] = 0xfe;
prefix[1] = 0x80;
if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:10]) {
// Link-local
return YES;
}
// fec0::/10
prefix[0] = 0xfe;
prefix[1] = 0xc0;
if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:10]) {
// Site local
return YES;
}
// fc00::/7
prefix[0] = 0xfc;
prefix[1] = 0x00;
if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:7]) {
// ULA
return YES;
}
return NO;
}
+ (NSString*)getBestIpv6Address:(NSArray<NSData*>*)addresses {
for (NSData* addrData in addresses) {
struct sockaddr_in6* sin6 = (struct sockaddr_in6*)[addrData bytes];
if (sin6->sin6_family != AF_INET6) {
continue;
}
if ([MDNSManager isLocalIpv6Address:addrData]) {
// Skip non-global addresses
continue;
}
uint8_t* addrBytes = sin6->sin6_addr.s6_addr;
uint8_t prefix[2];
// 2002::/16
prefix[0] = 0x20;
prefix[1] = 0x02;
if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:16]) {
Log(LOG_I, @"Ignoring 6to4 address: %@", [MDNSManager sockAddrToString:addrData]);
continue;
}
// 2001::/32
prefix[0] = 0x20;
prefix[1] = 0x01;
if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:32]) {
Log(LOG_I, @"Ignoring Teredo address: %@", [MDNSManager sockAddrToString:addrData]);
continue;
}
return [MDNSManager sockAddrToString:addrData];
}
return nil;
}
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
Log(LOG_D, @"Resolved address: %@ -> %@", service, service.hostName);
NSArray<NSData*>* addresses = [service addresses];
for (NSData* addrData in addresses) {
Log(LOG_I, @"Resolved address: %@ -> %@", [service hostName], [MDNSManager sockAddrToString: addrData]);
}
TemporaryHost* host = [[TemporaryHost alloc] init];
// Since we discovered this host over mDNS, we know we're on the same network
// as the PC and we can use our current WAN address as a likely candidate
// for our PC's external address.
struct in_addr wanAddr;
int err = LiFindExternalAddressIP4("stun.moonlight-stream.org", 3478, &wanAddr.s_addr);
if (err == 0) {
char addrStr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &wanAddr, addrStr, sizeof(addrStr));
host.externalAddress = [NSString stringWithFormat: @"%s", addrStr];
}
else {
Log(LOG_E, @"STUN failed to get WAN address: %d", err);
// First, look for an IPv4 record for the local address
for (NSData* addrData in addresses) {
struct sockaddr_in* sin = (struct sockaddr_in*)[addrData bytes];
if (sin->sin_family != AF_INET) {
continue;
}
// Since we discovered this host over IPv4 mDNS, we know we're on the same network
// as the PC and we can use our current WAN address as a likely candidate
// for our PC's external address.
struct in_addr wanAddr;
int err = LiFindExternalAddressIP4("stun.moonlight-stream.org", 3478, &wanAddr.s_addr);
if (err == 0) {
char addrStr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &wanAddr, addrStr, sizeof(addrStr));
host.externalAddress = [NSString stringWithFormat: @"%s", addrStr];
Log(LOG_I, @"External IPv4 address (STUN): %@ -> %@", [service hostName], host.externalAddress);
}
else {
Log(LOG_E, @"STUN failed to get WAN address: %d", err);
}
host.localAddress = [MDNSManager sockAddrToString:addrData];
Log(LOG_I, @"Local address chosen: %@ -> %@", [service hostName], host.localAddress);
break;
}
host.activeAddress = host.localAddress = service.hostName;
if (host.localAddress == nil) {
// If we didn't find an IPv4 record, look for a local IPv6 record
for (NSData* addrData in addresses) {
if ([MDNSManager isLocalIpv6Address:addrData]) {
host.localAddress = [MDNSManager sockAddrToString:addrData];
Log(LOG_I, @"Local address chosen: %@ -> %@", [service hostName], host.localAddress);
break;
}
}
}
host.ipv6Address = [MDNSManager getBestIpv6Address:addresses];
Log(LOG_I, @"IPv6 address chosen: %@ -> %@", [service hostName], host.ipv6Address);
host.activeAddress = host.localAddress;
host.name = service.hostName;
[self.callback updateHost:host];
});
+35 -14
View File
@@ -39,7 +39,7 @@
}
if ([serverInfoResp isStatusOk]) {
if ([[serverInfoResp getStringTag:@"state"] hasSuffix:@"_SERVER_BUSY"]) {
[_callback pairFailed:@"You must stop streaming before attempting to pair."];
[_callback pairFailed:@"You cannot pair while a previous session is still running on the host PC. Quit any running games or reboot the host PC, then try pairing again."];
} else if (![[serverInfoResp getStringTag:@"PairStatus"] isEqual:@"1"]) {
NSString* appversion = [serverInfoResp getStringTag:@"appversion"];
if (appversion == nil) {
@@ -53,10 +53,32 @@
}
}
// All codepaths must call pairFailed or pairSuccessful exactly once before returning!
- (void) finishPairing:(UIBackgroundTaskIdentifier)bgId withError:(NSString*)errorMsg {
if (bgId != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:bgId];
}
[_callback pairFailed:errorMsg];
}
- (void) finishPairing:(UIBackgroundTaskIdentifier)bgId withSuccess:(NSData*)derCertBytes {
if (bgId != UIBackgroundTaskInvalid) {
[[UIApplication sharedApplication] endBackgroundTask:bgId];
}
[_callback pairSuccessful:derCertBytes];
}
// All codepaths must call finishPairing exactly once before returning!
- (void) initiatePair:(int)serverMajorVersion {
Log(LOG_I, @"Pairing with generation %d server", serverMajorVersion);
// Start a background task to help prevent the app from being killed
// while pairing is in progress.
UIBackgroundTaskIdentifier bgId = [[UIApplication sharedApplication] beginBackgroundTaskWithName:@"Pairing PC" expirationHandler:^{
Log(LOG_W, @"Background pairing time has expired!");
}];
NSString* PIN = [self generatePIN];
NSData* salt = [self saltPIN:PIN];
Log(LOG_I, @"PIN: %@, saltedPIN: %@", PIN, salt);
@@ -65,18 +87,18 @@
HttpResponse* pairResp = [[HttpResponse alloc] init];
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:pairResp withUrlRequest:[_httpManager newPairRequest:salt clientCert:_clientCert]]];
if (![self verifyResponseStatus:pairResp]) {
[_callback pairFailed:@"Pairing stage #1 failed"];
[self finishPairing:bgId withError:@"Pairing stage #1 failed"];
return;
}
NSInteger pairedStatus;
if (![pairResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) {
[_callback pairFailed:@"Pairing was declined by the target."];
[self finishPairing:bgId withError:@"Pairing was declined by the target."];
return;
}
NSString* plainCert = [pairResp getStringTag:@"plaincert"];
if ([plainCert length] == 0) {
[_callback pairFailed:@"Another pairing attempt is already in progress."];
[self finishPairing:bgId withError:@"Another pairing attempt is already in progress."];
return;
}
@@ -107,7 +129,7 @@
![challengeResp getIntTag:@"paired" value:&pairedStatus] ||
pairedStatus != 1) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Pairing stage #2 failed"];
[self finishPairing:bgId withError:@"Pairing stage #2 failed"];
return;
}
@@ -134,7 +156,7 @@
![secretResp getIntTag:@"paired" value:&pairedStatus] ||
pairedStatus != 1) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Pairing stage #3 failed"];
[self finishPairing:bgId withError:@"Pairing stage #3 failed"];
return;
}
@@ -144,7 +166,7 @@
if (![cryptoMan verifySignature:serverSecret withSignature:serverSignature andCert:[Utils hexToBytes:plainCert]]) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Server certificate invalid"];
[self finishPairing:bgId withError:@"Server certificate invalid"];
return;
}
@@ -158,7 +180,7 @@
}
if (![serverChallengeRespHash isEqual:serverResponse]) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Incorrect PIN"];
[self finishPairing:bgId withError:@"Incorrect PIN"];
return;
}
@@ -169,7 +191,7 @@
![clientSecretResp getIntTag:@"paired" value:&pairedStatus] ||
pairedStatus != 1) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Pairing stage #4 failed"];
[self finishPairing:bgId withError:@"Pairing stage #4 failed"];
return;
}
@@ -179,21 +201,20 @@
![clientPairChallengeResp getIntTag:@"paired" value:&pairedStatus] ||
pairedStatus != 1) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Pairing stage #5 failed"];
[self finishPairing:bgId withError:@"Pairing stage #5 failed"];
return;
}
[_callback pairSuccessful: derCertBytes];
[self finishPairing:bgId withSuccess:derCertBytes];
}
// Caller calls finishPairing for us on failure
- (BOOL) verifyResponseStatus:(HttpResponse*)resp {
if (resp == nil) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:@"Network error occured."];
return false;
} else if (![resp isStatusOk]) {
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
[_callback pairFailed:resp.statusMessage];
return false;
} else {
return true;
+6 -1
View File
@@ -19,11 +19,16 @@
- (void) populateHost:(TemporaryHost*)host {
host.name = [[self getStringTag:TAG_HOSTNAME] trim];
host.localAddress = [[self getStringTag:TAG_LOCAL_IP] trim];
host.uuid = [[self getStringTag:TAG_UNIQUE_ID] trim];
host.mac = [[self getStringTag:TAG_MAC_ADDRESS] trim];
host.currentGame = [[self getStringTag:TAG_CURRENT_GAME] trim];
// We might get an IPv4 loopback address if we're using GS IPv6 Forwarder
NSString *lanAddr = [[self getStringTag:TAG_LOCAL_IP] trim];
if (![lanAddr hasPrefix:@"127."]) {
host.localAddress = lanAddr;
}
// Modern GFE versions don't actually give us a WAN address anymore
// so we leave the one that we populated from mDNS discovery via STUN.
NSString *wanAddr = [[self getStringTag:TAG_EXTERNAL_IP] trim];
+4 -2
View File
@@ -33,7 +33,7 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
+ (void) wakeHost:(TemporaryHost*)host {
NSData* wolPayload = [WakeOnLanManager createPayload:host];
for (int i = 0; i < 4; i++) {
for (int i = 0; i < 5; i++) {
const char* address;
struct addrinfo hints, *res, *curr;
@@ -44,7 +44,9 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
address = [host.externalAddress UTF8String];
} else if (i == 2 && host.address != nil) {
address = [host.address UTF8String];
} else if (i == 3) {
} else if (i == 3 && host.ipv6Address != nil) {
address = [host.ipv6Address UTF8String];
} else if (i == 4) {
address = "255.255.255.255";
} else {
// Requested address wasn't present
+1
View File
@@ -18,6 +18,7 @@
- (void) stageFailed:(const char*)stageName withError:(long)errorCode;
- (void) launchFailed:(NSString*)message;
- (void) rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor;
- (void) connectionStatusUpdate:(int)status;
@end
+61 -22
View File
@@ -32,15 +32,13 @@ static id<ConnectionCallbacks> _callbacks;
#define OUTPUT_BUS 0
#define MAX_CHANNEL_COUNT 2
#define FRAME_SIZE 240
#define CIRCULAR_BUFFER_SIZE 32
#define CIRCULAR_BUFFER_SIZE 16
static int audioBufferWriteIndex;
static int audioBufferReadIndex;
static int activeChannelCount;
static short audioCircularBuffer[CIRCULAR_BUFFER_SIZE][FRAME_SIZE * MAX_CHANNEL_COUNT];
static int audioBufferStride;
static int audioSamplesPerFrame;
static short* audioCircularBuffer;
#define AUDIO_QUEUE_BUFFERS 4
@@ -50,10 +48,15 @@ static VideoDecoderRenderer* renderer;
int DrDecoderSetup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags)
{
[renderer setupWithVideoFormat:videoFormat];
[renderer setupWithVideoFormat:videoFormat refreshRate:redrawRate];
return 0;
}
void DrCleanup(void)
{
[renderer cleanup];
}
int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
{
int offset = 0;
@@ -90,15 +93,16 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
{
int err;
// Clear the circular buffer
// Initialize the circular buffer
audioBufferWriteIndex = audioBufferReadIndex = 0;
audioSamplesPerFrame = opusConfig->samplesPerFrame;
audioBufferStride = opusConfig->channelCount * opusConfig->samplesPerFrame;
audioCircularBuffer = malloc(CIRCULAR_BUFFER_SIZE * audioBufferStride * sizeof(short));
if (audioCircularBuffer == NULL) {
Log(LOG_E, @"Error allocating output queue\n");
return -1;
}
// We only support stereo for now.
// TODO: Ensure AudioToolbox's channel mapping matches Opus's
// and correct if neccessary.
assert(audioConfiguration == AUDIO_CONFIGURATION_STEREO);
activeChannelCount = opusConfig->channelCount;
opusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
opusConfig->channelCount,
opusConfig->streams,
@@ -113,9 +117,11 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
[audioSession setPreferredSampleRate:opusConfig->sampleRate error:&audioSessionError];
[audioSession setCategory: AVAudioSessionCategoryPlayback error: &audioSessionError];
[audioSession setPreferredOutputNumberOfChannels:opusConfig->channelCount error:&audioSessionError];
[audioSession setPreferredIOBufferDuration:0.005 error:&audioSessionError];
[audioSession setActive: YES error: &audioSessionError];
// FIXME: Calling this breaks surround audio for some reason
//[audioSession setPreferredOutputNumberOfChannels:opusConfig->channelCount error:&audioSessionError];
#endif
OSStatus status;
@@ -137,8 +143,29 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
return status;
}
// We need to specify a channel layout for surround sound configurations
if (opusConfig->channelCount > 2) {
AudioChannelLayout channelLayout = {};
switch (opusConfig->channelCount) {
case 6:
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
break;
default:
// Unsupported channel layout
Log(LOG_E, @"Unsupported channel layout: %d\n", opusConfig->channelCount);
abort();
}
status = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_ChannelLayout, &channelLayout, sizeof(channelLayout));
if (status != noErr) {
Log(LOG_E, @"Error configuring surround channel layout: %d\n", status);
return status;
}
}
for (int i = 0; i < AUDIO_QUEUE_BUFFERS; i++) {
status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * FRAME_SIZE, &audioBuffers[i]);
status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * opusConfig->samplesPerFrame, &audioBuffers[i]);
if (status != noErr) {
Log(LOG_E, @"Error allocating output buffer: %d\n", status);
return status;
@@ -170,10 +197,15 @@ void ArCleanup(void)
// Also frees buffers
AudioQueueDispose(audioQueue, true);
// Must be freed after the queue is stopped
if (audioCircularBuffer != NULL) {
free(audioCircularBuffer);
audioCircularBuffer = NULL;
}
#if TARGET_OS_IPHONE
// Audio session is now inactive
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
[audioSession setActive: YES error: nil];
[[AVAudioSession sharedInstance] setActive: NO error: nil];
#endif
}
@@ -188,7 +220,7 @@ void ArDecodeAndPlaySample(char* sampleData, int sampleLength)
}
decodeLen = opus_multistream_decode(opusDecoder, (unsigned char *)sampleData, sampleLength,
audioCircularBuffer[audioBufferWriteIndex], FRAME_SIZE, 0);
(short*)&audioCircularBuffer[audioBufferWriteIndex * audioBufferStride], audioSamplesPerFrame, 0);
if (decodeLen > 0) {
// Use a full memory barrier to ensure the circular buffer is written before incrementing the index
__sync_synchronize();
@@ -238,6 +270,11 @@ void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsi
[_callbacks rumble:controllerNumber lowFreqMotor:lowFreqMotor highFreqMotor:highFreqMotor];
}
void ClConnectionStatusUpdate(int status)
{
[_callbacks connectionStatusUpdate:status];
}
-(void) terminate
{
// Interrupt any action blocking LiStartConnection(). This is
@@ -327,7 +364,7 @@ void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsi
// Additionally, iPhone X had a bug which would cause video
// to freeze after a few minutes with HEVC prior to iOS 11.3.
// As a result, we will only use HEVC on iOS 11.3 or later.
if (@available(iOS 11.3, *)) {
if (@available(iOS 11.3, tvOS 11.3, *)) {
_streamConfig.supportsHevc = config.allowHevc && VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC);
}
#else
@@ -348,6 +385,7 @@ void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsi
LiInitializeVideoCallbacks(&_drCallbacks);
_drCallbacks.setup = DrDecoderSetup;
_drCallbacks.cleanup = DrCleanup;
_drCallbacks.submitDecodeUnit = DrSubmitDecodeUnit;
// RFI doesn't work properly with HEVC on iOS 11 with an iPhone SE (at least)
@@ -369,6 +407,7 @@ void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsi
_clCallbacks.connectionTerminated = ClConnectionTerminated;
_clCallbacks.logMessage = ClLogMessage;
_clCallbacks.rumble = ClRumble;
_clCallbacks.connectionStatusUpdate = ClConnectionStatusUpdate;
return self;
}
@@ -376,7 +415,7 @@ void ClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsi
static void FillOutputBuffer(void *aqData,
AudioQueueRef inAQ,
AudioQueueBufferRef inBuffer) {
inBuffer->mAudioDataByteSize = activeChannelCount * FRAME_SIZE * sizeof(short);
inBuffer->mAudioDataByteSize = audioBufferStride * sizeof(short);
assert(inBuffer->mAudioDataByteSize == inBuffer->mAudioDataBytesCapacity);
@@ -384,7 +423,7 @@ static void FillOutputBuffer(void *aqData,
if (audioBufferWriteIndex != audioBufferReadIndex) {
// Copy data to the audio buffer
memcpy(inBuffer->mAudioData,
audioCircularBuffer[audioBufferReadIndex],
&audioCircularBuffer[audioBufferReadIndex * audioBufferStride],
inBuffer->mAudioDataByteSize);
// Use a full memory barrier to ensure the circular buffer is read before incrementing the index
+3 -1
View File
@@ -12,7 +12,9 @@
- (id)initWithView:(OSView*)view;
- (void)setupWithVideoFormat:(int)videoFormat;
- (void)setupWithVideoFormat:(int)videoFormat refreshRate:(int)refreshRate;
- (void)cleanup;
- (void)updateBufferForRange:(CMBlockBufferRef)existingBuffer data:(unsigned char *)data offset:(int)offset length:(int)nalLength;
+28 -3
View File
@@ -20,6 +20,8 @@
NSData *spsData, *ppsData, *vpsData;
CMVideoFormatDescriptionRef formatDesc;
CADisplayLink* _displayLink;
}
- (void)reinitializeDisplayLayer
@@ -70,12 +72,33 @@
return self;
}
- (void)setupWithVideoFormat:(int)videoFormat
- (void)setupWithVideoFormat:(int)videoFormat refreshRate:(int)refreshRate
{
self->videoFormat = videoFormat;
#if !TARGET_OS_IPHONE
_view.codec = videoFormat;
#endif
if (refreshRate > 60) {
// HACK: We seem to just get 60 Hz screen updates even with a 120 FPS stream if
// we don't set preferredFramesPerSecond somewhere. Since we're a UIKit view, we
// have to use CADisplayLink for that. See https://github.com/moonlight-stream/moonlight-ios/issues/372
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallback:)];
if (@available(iOS 10.0, tvOS 10.0, *)) {
_displayLink.preferredFramesPerSecond = refreshRate;
}
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
}
- (void)displayLinkCallback:(CADisplayLink *)sender
{
// No-op - rendering done in submitDecodeBuffer
}
- (void)cleanup
{
[_displayLink invalidate];
}
#define FRAME_START_PREFIX_SIZE 4
@@ -356,8 +379,10 @@
// Enqueue the next frame
[self->displayLayer enqueueSampleBuffer:sampleBuffer];
// Ensure the layer is visible now
self->displayLayer.hidden = NO;
if ([self isNalReferencePicture:nalType]) {
// Ensure the layer is visible now
self->displayLayer.hidden = NO;
}
// Dereference the buffers
CFRelease(blockBuffer);
+49 -52
View File
@@ -66,33 +66,51 @@ static UIImage* noImage;
[_callback appClicked:_app];
}
#if TARGET_OS_TV
- (UIImageView*) renderToImageView:(UIImage*)appImage {
//custom image to do TvOS hover popup effect
UIImageView *imageView = [[UIImageView alloc] initWithImage:appImage];
imageView.userInteractionEnabled = YES;
imageView.adjustsImageWhenAncestorFocused = YES;
imageView.frame = self.frame;
UIGraphicsBeginImageContextWithOptions(self.frame.size, false, 0);
[imageView.layer renderInContext:(UIGraphicsGetCurrentContext())];
[_appLabel.layer renderInContext:(UIGraphicsGetCurrentContext())];
[_appOverlay.layer renderInContext:(UIGraphicsGetCurrentContext())];
UIImage *imageWithText = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[imageView setImage:imageWithText];
[_appButton addSubview:imageView];
return imageView;
}
#endif
- (void) updateAppImage {
if (_appOverlay != nil) {
[_appOverlay removeFromSuperview];
_appOverlay = nil;
}
_appButton.frame = CGRectMake(0, 0, noImage.size.width, noImage.size.height);
self.frame = CGRectMake(0, 0, noImage.size.width, noImage.size.height);
#if TARGET_OS_TV
_appButton.frame = CGRectMake(0, 0, 200, 265);
#else
_appButton.frame = CGRectMake(0, 0, 150, 200);
#endif
self.frame = _appButton.frame;
if ([_app.id isEqualToString:_app.host.currentGame]) {
#if TARGET_OS_TV
_appButton.layer.masksToBounds = NO;
_appButton.layer.shadowColor = [UIColor greenColor].CGColor;
_appButton.layer.shadowOffset = CGSizeMake(5,8);
_appButton.layer.shadowOpacity = 0.7;
#else
// Only create the app overlay if needed
_appOverlay = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Play"]];
_appOverlay.layer.shadowColor = [UIColor blackColor].CGColor;
_appOverlay.layer.shadowOffset = CGSizeMake(0, 0);
_appOverlay.layer.shadowOpacity = 1;
_appOverlay.layer.shadowRadius = 2.0;
[self addSubview:_appOverlay];
_appOverlay.frame = CGRectMake(0, 0, noImage.size.width / 2.f, noImage.size.height / 4.f);
[_appOverlay setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/6)];
_appOverlay.layer.shadowRadius = 4.0;
#if TARGET_OS_TV
_appOverlay.contentMode = UIViewContentModeScaleAspectFit;
_appOverlay.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height / 3.f);
#else
_appOverlay.frame = CGRectMake(self.frame.size.width / 4, 10, self.frame.size.width / 2, self.frame.size.height / 4.f);
#endif
}
@@ -112,24 +130,13 @@ static UIImage* noImage;
// TODO: Improve no-app image detection
if (!(appImage.size.width == 130.f && appImage.size.height == 180.f) && // GFE 2.0
!(appImage.size.width == 628.f && appImage.size.height == 888.f)) { // GFE 3.0
#if TARGET_OS_TV
//custom image to do TvOS hover popup effect
UIImageView *imageView = [[UIImageView alloc] initWithImage:appImage];
imageView.userInteractionEnabled = YES;
imageView.adjustsImageWhenAncestorFocused = YES;
imageView.frame = CGRectMake(0, 0, 200, 265);
[_appButton addSubview:imageView];
_appButton.frame = CGRectMake(0, 0, 200, 265);
self.frame = CGRectMake(0, 0, 200, 265);
#if TARGET_OS_TV
[_appButton addSubview:[self renderToImageView:appImage]];
#else
_appButton.frame = CGRectMake(0, 0, appImage.size.width / 2, appImage.size.height / 2);
self.frame = CGRectMake(0, 0, appImage.size.width / 2, appImage.size.height / 2);
_appOverlay.frame = CGRectMake(0, 0, self.frame.size.width / 2.f, self.frame.size.height / 4.f);
_appOverlay.layer.shadowRadius = 4.0;
[_appOverlay setCenter:CGPointMake(self.frame.size.width/2, self.frame.size.height/6)];
[self addSubview:_appOverlay];
#endif
[_appButton setBackgroundImage:appImage forState:UIControlStateNormal];
[self setNeedsDisplay];
} else {
@@ -140,35 +147,27 @@ static UIImage* noImage;
}
if (noAppImage) {
#if TARGET_OS_TV
_appLabel = [[UILabel alloc] init];
[_appLabel setTextColor:[UIColor whiteColor]];
[_appLabel setText:_app.name];
[_appLabel setFrame:self.frame];
#else
_appLabel = _appButton.titleLabel;
[_appButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[_appButton setTitle:_app.name forState:UIControlStateNormal];
[_appButton setTitleEdgeInsets:UIEdgeInsetsMake(_appOverlay != nil ? _appOverlay.frame.size.height : 5, 5, 5, 5)];
#endif
[_appLabel setFont:[UIFont systemFontOfSize:24]];
[_appLabel setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
[_appLabel setTextAlignment:NSTextAlignmentCenter];
[_appLabel setLineBreakMode:NSLineBreakByWordWrapping];
[_appLabel setNumberOfLines:0];
[_appLabel setText:_app.name];
#if TARGET_OS_TV
[_appLabel setFont:[UIFont systemFontOfSize:16]];
[_appLabel setAdjustsFontSizeToFitWidth:YES];
[_appLabel setFrame: CGRectMake(0, 0, 200, 265)];
//custom image to do TvOS hover popup effect
UIImageView *imageView = [[UIImageView alloc] initWithImage:appImage];
imageView.userInteractionEnabled = YES;
imageView.adjustsImageWhenAncestorFocused = YES;
imageView.frame = CGRectMake(0, 0, 200, 265);
UIGraphicsBeginImageContextWithOptions(_appLabel.frame.size, false, 0);
[imageView.layer renderInContext:(UIGraphicsGetCurrentContext())];
[_appLabel.layer renderInContext:(UIGraphicsGetCurrentContext())];
UIImage *imageWithText = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[imageView setImage:imageWithText];
[_appButton addSubview:imageView];
_appButton.frame = CGRectMake(0, 0, 200, 265);
self.frame = CGRectMake(0, 0, 200, 265);
[_appButton addSubview:[self renderToImageView:noImage]];
#else
CGFloat padding = 4.f;
[_appLabel setFrame: CGRectMake(padding, padding, _appButton.frame.size.width - 2 * padding, _appButton.frame.size.height - 2 * padding)];
[_appButton addSubview:_appLabel];
[self addSubview:_appOverlay];
#endif
}
@@ -182,9 +181,7 @@ static UIImage* noImage;
// Update the app image if neccessary
if ((_appOverlay != nil && ![_app.id isEqualToString:_app.host.currentGame]) ||
(_appOverlay == nil && [_app.id isEqualToString:_app.host.currentGame])) {
#if !TARGET_OS_TV
[self updateAppImage];
#endif
}
// Stop updating when we detach from our parent view
+66 -68
View File
@@ -12,16 +12,18 @@
TemporaryHost* _host;
UIButton* _hostButton;
UILabel* _hostLabel;
UILabel* _hostStatus;
UILabel* _hostPairState;
UIImageView* _hostOverlay;
UIActivityIndicatorView* _hostSpinner;
id<HostCallback> _callback;
CGSize _labelSize;
}
static const float REFRESH_CYCLE = 2.0f;
#if TARGET_OS_TV
static const int ITEM_PADDING = 50;
static const int LABEL_DY = 40;
#else
static const int ITEM_PADDING = 0;
static const int LABEL_DY = 20;
#endif
@@ -32,53 +34,70 @@ static const int LABEL_DY = 20;
[_hostButton setBackgroundImage:[UIImage imageNamed:@"Computer"] forState:UIControlStateNormal];
[_hostButton sizeToFit];
_hostButton.layer.shadowColor = [[UIColor blackColor] CGColor];
#if TARGET_OS_TV
_hostButton.frame = CGRectMake(0, 0, 400, 400);
#else
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
_hostButton.frame = CGRectMake(0, 0, 200, 200);
} else {
_hostButton.frame = CGRectMake(0, 0, 100, 100);
}
#endif
_hostButton.layer.shadowColor = [[UIColor blackColor] CGColor];
_hostButton.layer.shadowOffset = CGSizeMake(5,8);
_hostButton.layer.shadowOpacity = 0.7;
_hostButton.layer.shadowOpacity = 0.3;
[_hostButton addTarget:self action:@selector(hostButtonSelected:) forControlEvents:UIControlEventTouchDown];
[_hostButton addTarget:self action:@selector(hostButtonDeselected:) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchCancel | UIControlEventTouchDragExit];
_hostLabel = [[UILabel alloc] init];
_hostStatus = [[UILabel alloc] init];
_hostPairState = [[UILabel alloc] init];
_hostLabel.textColor = [UIColor whiteColor];
_hostOverlay = [[UIImageView alloc] initWithFrame:CGRectMake(_hostButton.frame.size.width / 3, _hostButton.frame.size.height / 4, _hostButton.frame.size.width / 3, _hostButton.frame.size.height / 3)];
_hostSpinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
[_hostSpinner setFrame:_hostOverlay.frame];
_hostSpinner.userInteractionEnabled = NO;
_hostSpinner.hidesWhenStopped = YES;
[self addSubview:_hostButton];
[self addSubview:_hostLabel];
[self addSubview:_hostOverlay];
[self addSubview:_hostSpinner];
return self;
}
- (void) hostButtonSelected:(id)sender {
_hostSpinner.layer.opacity = 0.5f;
_hostOverlay.layer.opacity = 0.5f;
}
- (void) hostButtonDeselected:(id)sender {
_hostSpinner.layer.opacity = 1.0f;
_hostOverlay.layer.opacity = 1.0f;
}
- (id) initForAddWithCallback:(id<HostCallback>)callback {
self = [self init];
_callback = callback;
[_hostButton setBackgroundImage:[UIImage imageNamed:@"Computer"] forState:UIControlStateNormal];
[_hostButton setContentEdgeInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
if (@available(iOS 9.0, tvOS 9.0, *)) {
[_hostButton addTarget:self action:@selector(addClicked) forControlEvents:UIControlEventPrimaryActionTriggered];
}
else {
[_hostButton addTarget:self action:@selector(addClicked) forControlEvents:UIControlEventTouchUpInside];
}
[_hostButton sizeToFit];
[_hostLabel setText:@"Add Host"];
[_hostLabel sizeToFit];
#if !TARGET_OS_TV
_hostLabel.textColor = [UIColor whiteColor];
#endif
_hostLabel.center = CGPointMake(_hostButton.frame.origin.x + (_hostButton.frame.size.width / 2), _hostButton.frame.origin.y + _hostButton.frame.size.height + LABEL_DY);
UIImageView* addIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"AddComputerIcon"]];
[addIcon sizeToFit];
addIcon.center = CGPointMake(_hostButton.frame.origin.x + _hostButton.frame.size.width, _hostButton.frame.origin.y);
float x = _hostButton.frame.origin.x + _hostButton.frame.size.width / 2;
_hostLabel.center = CGPointMake(x, _hostButton.frame.origin.y + _hostButton.frame.size.height + LABEL_DY);
// This is required to ensure this button is the same size as the others
_hostPairState.text = @"None";
_hostStatus.text = @"None";
[_hostPairState sizeToFit];
[_hostStatus sizeToFit];
[_hostOverlay setImage:[UIImage imageNamed:@"AddOverlayIcon"]];
[self updateBounds];
[self addSubview:_hostButton];
[self addSubview:_hostLabel];
[self addSubview:addIcon];
return self;
}
@@ -99,10 +118,6 @@ static const int LABEL_DY = 20;
[self updateContentsForHost:host];
[self updateBounds];
[self addSubview:_hostButton];
[self addSubview:_hostLabel];
[self addSubview:_hostStatus];
[self addSubview:_hostPairState];
[self startUpdateLoop];
return self;
@@ -116,65 +131,44 @@ static const int LABEL_DY = 20;
x = MIN(x, _hostButton.frame.origin.x);
x = MIN(x, _hostLabel.frame.origin.x);
x = MIN(x, _hostStatus.frame.origin.x);
x = MIN(x, _hostPairState.frame.origin.x);
y = MIN(y, _hostButton.frame.origin.y);
y = MIN(y, _hostLabel.frame.origin.y);
y = MIN(y, _hostStatus.frame.origin.y);
y = MIN(y, _hostPairState.frame.origin.y);
width = MAX(width, _hostButton.frame.size.width);
width = MAX(width, _hostLabel.frame.size.width);
width = MAX(width, _hostStatus.frame.size.width);
width = MAX(width, _hostPairState.frame.size.width);
height = _hostButton.frame.size.height +
_hostLabel.frame.size.height +
_hostStatus.frame.size.height +
_hostPairState.frame.size.height +
LABEL_DY / 2;
self.bounds = CGRectMake(x, y, width, height);
self.frame = CGRectMake(x, y, width, height);
self.bounds = CGRectMake(x - ITEM_PADDING, y - ITEM_PADDING, width + 2 * ITEM_PADDING, height + 2 * ITEM_PADDING);
}
- (void) updateContentsForHost:(TemporaryHost*)host {
_hostLabel.text = _host.name;
#if !TARGET_OS_TV
_hostLabel.textColor = [UIColor whiteColor];
#endif
[_hostLabel sizeToFit];
switch (host.pairState) {
case PairStateUnknown:
_hostPairState.text = @"Pair State Unknown";
break;
case PairStateUnpaired:
_hostPairState.text = @"Not Paired";
break;
case PairStatePaired:
_hostPairState.text = @"Paired";
break;
if (host.state == StateOnline) {
[_hostSpinner stopAnimating];
if (host.pairState == PairStateUnpaired) {
[_hostOverlay setImage:[UIImage imageNamed:@"LockedOverlayIcon"]];
}
else {
[_hostOverlay setImage:nil];
}
}
#if !TARGET_OS_TV
_hostPairState.textColor = [UIColor whiteColor];
#endif
[_hostPairState sizeToFit];
if (host.online) {
_hostStatus.text = @"Online";
_hostStatus.textColor = [UIColor greenColor];
} else {
_hostStatus.text = @"Offline";
_hostStatus.textColor = [UIColor grayColor];
else if (host.state == StateOffline) {
[_hostSpinner stopAnimating];
[_hostOverlay setImage:[UIImage imageNamed:@"ErrorOverlayIcon"]];
}
else {
[_hostSpinner startAnimating];
}
[_hostStatus sizeToFit];
float x = _hostButton.frame.origin.x + _hostButton.frame.size.width / 2;
_hostLabel.center = CGPointMake(x, _hostButton.frame.origin.y + _hostButton.frame.size.height + LABEL_DY);
_hostPairState.center = CGPointMake(x, _hostLabel.center.y + LABEL_DY);
_hostStatus.center = CGPointMake(x, _hostPairState.center.y + LABEL_DY);
}
- (void) startUpdateLoop {
@@ -209,8 +203,12 @@ static const int LABEL_DY = 20;
UIButton *previousButton = (UIButton *)context.previouslyFocusedItem;
UIButton *nextButton = (UIButton *) context.nextFocusedItem;
previousButton.layer.shadowColor = [[UIColor blackColor] CGColor];
nextButton.layer.shadowColor = [[UIColor greenColor] CGColor];
if (previousButton.superview == self) {
self.backgroundColor = nil;
}
if (nextButton.superview == self) {
nextButton.superview.backgroundColor = [UIColor darkGrayColor];
}
}
#endif
+6
View File
@@ -14,6 +14,12 @@ typedef NS_ENUM(int, PairState) {
PairStatePaired
};
typedef NS_ENUM(int, State) {
StateUnknown,
StateOffline,
StateOnline
};
FOUNDATION_EXPORT NSString *const deviceName;
+ (NSData*) randomBytes:(NSInteger)length;
@@ -18,8 +18,8 @@
@interface MainFrameViewController : UICollectionViewController <DiscoveryCallback, PairCallback, HostCallback, AppCallback, AppAssetCallback, NSURLConnectionDelegate, SWRevealViewControllerDelegate>
#if !TARGET_OS_TV
@property (strong, nonatomic) IBOutlet UIButton *limelightLogoButton;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *computerNameButton;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *settingsButton;
@property (weak, nonatomic) IBOutlet UIBarButtonItem *upButton;
#endif
@end
@@ -44,14 +44,12 @@
UIAlertController* _pairAlert;
LoadingFrameViewController* _loadingFrame;
UIScrollView* hostScrollView;
int currentPosition;
FrontViewPosition currentPosition;
NSArray* _sortedAppList;
NSCache* _boxArtCache;
bool _background;
#if TARGET_OS_TV
UITapGestureRecognizer* _menuRecognizer;
#else
UIButton* _pullArrow;
#endif
}
static NSMutableSet* hostList;
@@ -111,6 +109,18 @@ static NSMutableSet* hostList;
});
}
- (void)disableUpButton {
#if !TARGET_OS_TV
[self->_upButton setTitle:nil];
#endif
}
- (void)enableUpButton {
#if !TARGET_OS_TV
[self->_upButton setTitle:@"Select New Host"];
#endif
}
- (void)alreadyPaired {
BOOL usingCachedAppList = false;
@@ -126,13 +136,9 @@ static NSMutableSet* hostList;
return;
}
#if TARGET_OS_TV
self.title = host.name;
#else
self->_computerNameButton.title = host.name;
#endif
[self.navigationController.navigationBar setNeedsLayout];
[self enableUpButton];
[self updateAppsForHost:host];
[self hideLoadingFrame: nil];
});
@@ -162,7 +168,7 @@ static NSMutableSet* hostList;
[self hideLoadingFrame: ^{
[[self activeViewController] presentViewController:applistAlert animated:YES completion:nil];
}];
host.online = NO;
host.state = StateOffline;
[self showHostSelectionView];
});
} else {
@@ -174,12 +180,8 @@ static NSMutableSet* hostList;
return;
}
#if TARGET_OS_TV
self.title = host.name;
#else
self->_computerNameButton.title = host.name;
#endif
[self.navigationController.navigationBar setNeedsLayout];
[self enableUpButton];
[self updateAppsForHost:host];
[self->_appManager stopRetrieving];
@@ -251,11 +253,10 @@ static NSMutableSet* hostList;
[_appManager stopRetrieving];
_selectedHost = nil;
#if TARGET_OS_TV
self.title = @"Select Host";
#else
_computerNameButton.title = @"No Host Selected";
#endif
[self disableUpButton];
[self.collectionView reloadData];
[self.view addSubview:hostScrollView];
}
@@ -284,7 +285,7 @@ static NSMutableSet* hostList;
// This shows the context menu with wake, delete, etc. rather
// than just hanging for a while and failing as we would in this
// code path.
if (!host.online && view != nil) {
if (host.state != StateOnline && view != nil) {
[self hostLongClicked:host view:view];
return;
}
@@ -303,7 +304,7 @@ static NSMutableSet* hostList;
// should hit most. Check for a valid view because we don't want to hit the fast
// path after coming back from streaming, since we need to fetch serverinfo too
// so that our active game data is correct.
if (host.online && host.pairState == PairStatePaired && host.appList.count > 0 && view != nil) {
if (host.state == StateOnline && host.pairState == PairStatePaired && host.appList.count > 0 && view != nil) {
[self alreadyPaired];
return;
}
@@ -341,7 +342,7 @@ static NSMutableSet* hostList;
}
}];
host.online = NO;
host.state = StateOffline;
[self showHostSelectionView];
});
} else {
@@ -384,8 +385,32 @@ static NSMutableSet* hostList;
- (void)hostLongClicked:(TemporaryHost *)host view:(UIView *)view {
Log(LOG_D, @"Long clicked host: %@", host.name);
UIAlertController* longClickAlert = [UIAlertController alertControllerWithTitle:host.name message:@"" preferredStyle:UIAlertControllerStyleActionSheet];
if (!host.online) {
NSString* message;
switch (host.state) {
case StateOffline:
message = @"Offline";
break;
case StateOnline:
if (host.pairState == PairStatePaired) {
message = @"Online - Paired";
}
else {
message = @"Online - Not Paired";
}
break;
case StateUnknown:
message = @"Connecting";
break;
default:
break;
}
UIAlertController* longClickAlert = [UIAlertController alertControllerWithTitle:host.name message:message preferredStyle:UIAlertControllerStyleActionSheet];
if (host.state != StateOnline) {
[longClickAlert addAction:[UIAlertAction actionWithTitle:@"Wake" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
UIAlertController* wolAlert = [UIAlertController alertControllerWithTitle:@"Wake On LAN" message:@"" preferredStyle:UIAlertControllerStyleAlert];
[wolAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
@@ -479,10 +504,17 @@ static NSMutableSet* hostList;
_streamConfig.multiController = streamSettings.multiController;
_streamConfig.gamepadMask = [ControllerSupport getConnectedGamepadMask:_streamConfig];
// TODO: Detect attached surround sound system then address 5.1 TODOs
// in Connection.m
_streamConfig.audioChannelCount = 2;
_streamConfig.audioChannelMask = 0x3;
// Probe for supported channel configurations
Log(LOG_I, @"Audio device supports %d channels", [AVAudioSession sharedInstance].maximumOutputNumberOfChannels);
if ([AVAudioSession sharedInstance].maximumOutputNumberOfChannels >= 6) {
_streamConfig.audioChannelCount = 6;
_streamConfig.audioChannelMask = 0xFC;
}
else {
_streamConfig.audioChannelCount = 2;
_streamConfig.audioChannelMask = 0x3;
}
// HDR requires HDR10 game, HDR10 display, and HEVC Main10 decoder on the client.
// It additionally requires an HEVC Main10 encoder on the server (GTX 1000+).
@@ -552,46 +584,40 @@ static NSMutableSet* hostList;
}
}
[self->_discMan addHostToDiscovery:app.host];
UIAlertController* alert;
// If it fails, display an error and stop the current operation
if (quitResponse.statusCode != 200) {
alert = [UIAlertController alertControllerWithTitle:@"Quitting App Failed"
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Quitting App Failed"
message:@"Failed to quit app. If this app was started by "
"another device, you'll need to quit from that device."
preferredStyle:UIAlertControllerStyleAlert];
}
// If it succeeds and we're to start streaming, segue to the stream and return
else if (![app.id isEqualToString:currentApp.id]) {
app.host.currentGame = @"0";
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateAppsForHost:app.host];
[self prepareToStreamApp:app];
[self hideLoadingFrame: ^{
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
[[self activeViewController] presentViewController:alert animated:YES completion:nil];
}];
});
return;
}
// Otherwise, display a dialog to notify the user that the app was quit
else {
app.host.currentGame = @"0";
alert = [UIAlertController alertControllerWithTitle:@"Quitting App"
message:@"The app was quit successfully."
preferredStyle:UIAlertControllerStyleAlert];
dispatch_async(dispatch_get_main_queue(), ^{
// Refresh the UI
[self updateAppsForHost:app.host];
// If it succeeds and we're to start streaming, segue to the stream
if (![app.id isEqualToString:currentApp.id]) {
[self prepareToStreamApp:app];
[self hideLoadingFrame: ^{
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
}];
}
else {
// Otherwise, just hide the loading icon
[self hideLoadingFrame:nil];
}
});
}
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateAppsForHost:app.host];
[self hideLoadingFrame: ^{
[[self activeViewController] presentViewController:alert animated:YES completion:nil];
}];
});
});
}];
@@ -620,30 +646,6 @@ static NSMutableSet* hostList;
[(SettingsViewController*)[revealController rearViewController] saveSettings];
}
// Fade out the pull arrow
[UIView animateWithDuration:0.1
animations:^{
self->_pullArrow.alpha = 0.0;
}
completion:^(BOOL finished) {
// Flip the direction of the arrow
if (position == FrontViewPositionLeft) {
// Change the pull arrow back to the default rotation
self->_pullArrow.imageView.transform = CGAffineTransformMakeRotation(0);
}
else {
// Flip the pull arrow when the reveal is toggled
self->_pullArrow.imageView.transform = CGAffineTransformMakeRotation(M_PI);
}
// Fade it back in
[UIView animateWithDuration:0.2
animations:^{
self->_pullArrow.alpha = 1.0;
}
completion:nil];
}];
currentPosition = position;
}
#endif
@@ -670,31 +672,53 @@ static NSMutableSet* hostList;
[_loadingFrame dismissLoadingFrame:completion];
}
- (void)adjustScrollViewForSafeArea:(UIScrollView*)view {
if (@available(iOS 11.0, *)) {
if (self.view.safeAreaInsets.left >= 20 || self.view.safeAreaInsets.right >= 20) {
view.contentInset = UIEdgeInsetsMake(0, 20, 0, 20);
}
}
}
// Adjust the subviews for the safe area on the iPhone X.
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
[self adjustScrollViewForSafeArea:self.collectionView];
[self adjustScrollViewForSafeArea:self->hostScrollView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.title = @"Select Host";
#if !TARGET_OS_TV
// Set the side bar button action. When it's tapped, it'll show the sidebar.
[_limelightLogoButton addTarget:self.revealViewController action:@selector(revealToggle:) forControlEvents:UIControlEventTouchDown];
[_settingsButton setTarget:self.revealViewController];
[_settingsButton setAction:@selector(revealToggle:)];
// Set the host name button action. When it's tapped, it'll show the host selection view.
[_computerNameButton setTarget:self];
[_computerNameButton setAction:@selector(showHostSelectionView)];
[_upButton setTarget:self];
[_upButton setAction:@selector(showHostSelectionView)];
[self disableUpButton];
// Set the gesture
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
// Get callbacks associated with the viewController
[self.revealViewController setDelegate:self];
// Disable bounce-back on reveal VC otherwise the settings will snap closed
// if the user drags all the way off the screen opposite the settings pane.
self.revealViewController.bounceBackOnOverdraw = NO;
#else
_menuRecognizer = [[UITapGestureRecognizer alloc] init];
[_menuRecognizer addTarget:self action: @selector(showHostSelectionView)];
_menuRecognizer.allowedPressTypes = [[NSArray alloc] initWithObjects:[NSNumber numberWithLong:UIPressTypeMenu], nil];
self.navigationController.navigationBar.titleTextAttributes = [NSDictionary dictionaryWithObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName];
self.title = @"Select Host";
#endif
_loadingFrame = [self.storyboard instantiateViewControllerWithIdentifier:@"loadingFrame"];
@@ -716,25 +740,12 @@ static NSMutableSet* hostList;
}
_boxArtCache = [[NSCache alloc] init];
[self setAutomaticallyAdjustsScrollViewInsets:NO];
hostScrollView = [[ComputerScrollView alloc] init];
hostScrollView.frame = CGRectMake(0, self.navigationController.navigationBar.frame.origin.y + self.navigationController.navigationBar.frame.size.height, self.view.frame.size.width, self.view.frame.size.height / 2);
[hostScrollView setShowsHorizontalScrollIndicator:NO];
hostScrollView.delaysContentTouches = NO;
#if !TARGET_OS_TV
_pullArrow = [[UIButton alloc] init];
[_pullArrow addTarget:self.revealViewController action:@selector(revealToggle:) forControlEvents:UIControlEventTouchDown];
[_pullArrow setImage:[UIImage imageNamed:@"PullArrow"] forState:UIControlStateNormal];
[_pullArrow sizeToFit];
_pullArrow.frame = CGRectMake(0,
self.collectionView.frame.size.height / 6 - _pullArrow.frame.size.height / 2 - self.navigationController.navigationBar.frame.size.height,
_pullArrow.frame.size.width,
_pullArrow.frame.size.height);
#endif
self.collectionView.delaysContentTouches = NO;
self.collectionView.allowsMultipleSelection = NO;
#if !TARGET_OS_TV
@@ -745,9 +756,6 @@ static NSMutableSet* hostList;
_discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self];
[self.view addSubview:hostScrollView];
#if !TARGET_OS_TV
[self.view addSubview:_pullArrow];
#endif
}
-(void)beginForegroundRefresh:(bool)refreshAppList
@@ -861,6 +869,9 @@ static NSMutableSet* hostList;
if (host.activeAddress == nil) {
host.activeAddress = host.address;
}
if (host.activeAddress == nil) {
host.activeAddress = host.ipv6Address;
}
}
}
}
@@ -869,7 +880,7 @@ static NSMutableSet* hostList;
dispatch_async(dispatch_get_main_queue(), ^{
Log(LOG_D, @"New host list:");
for (TemporaryHost* host in hosts) {
Log(LOG_D, @"Host: \n{\n\t name:%@ \n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", host.name, host.address, host.localAddress, host.externalAddress, host.uuid, host.mac, host.pairState, host.online, host.activeAddress);
Log(LOG_D, @"Host: \n{\n\t name:%@ \n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t ipv6Address:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", host.name, host.address, host.localAddress, host.externalAddress, host.ipv6Address, host.uuid, host.mac, host.pairState, host.state, host.activeAddress);
}
@synchronized(hostList) {
[hostList removeAllObjects];
@@ -911,10 +922,18 @@ static NSMutableSet* hostList;
}
- (float) getCompViewX:(UIComputerView*)comp addComp:(UIComputerView*)addComp prevEdge:(float)prevEdge {
float padding;
#if TARGET_OS_TV
padding = 100;
#else
padding = addComp.frame.size.width / 2;
#endif
if (prevEdge == -1) {
return hostScrollView.frame.origin.x + comp.frame.size.width / 2 + addComp.frame.size.width / 2;
return hostScrollView.frame.origin.x + comp.frame.size.width / 2 + padding;
} else {
return prevEdge + addComp.frame.size.width / 2 + comp.frame.size.width / 2;
return prevEdge + comp.frame.size.width / 2 + padding;
}
}
@@ -21,6 +21,14 @@
@property (strong, nonatomic) IBOutlet UISegmentedControl *hevcSelector;
@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunguarded-availability"
// This is okay because it's just an enum and access uses @available checks
@property(nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle;
#pragma clang diagnostic pop
- (void) saveSettings;
@end
@@ -10,10 +10,14 @@
#import "TemporarySettings.h"
#import "DataManager.h"
#import <VideoToolbox/VideoToolbox.h>
@implementation SettingsViewController {
NSInteger _bitrate;
Boolean _adjustedForSafeArea;
}
@dynamic overrideUserInterfaceStyle;
static NSString* bitrateFormat = @"Bitrate: %.1f Mbps";
static const int bitrateTable[] = {
500,
@@ -65,10 +69,12 @@ static const int bitrateTable[] = {
// highest view Y value to set our scroll view's content
// size.
for (UIView* view in self.scrollView.subviews) {
// UIScrollViews have 2 default UIImageView children
// UIScrollViews have 2 default child views
// which represent the horizontal and vertical scrolling
// indicators. Ignore these when computing content size.
if ([view isKindOfClass:[UIImageView class]]) {
// indicators. Ignore any views we don't recognize.
if (![view isKindOfClass:[UILabel class]] &&
![view isKindOfClass:[UISegmentedControl class]] &&
![view isKindOfClass:[UISlider class]]) {
continue;
}
@@ -82,26 +88,31 @@ static const int bitrateTable[] = {
self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width,
highestViewY + 20);
}
}
// Adjust the subviews for the safe area on the iPhone X.
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
// Adjust the subviews for the safe area on the iPhone X.
if (!_adjustedForSafeArea) {
if (@available(iOS 11.0, *)) {
for (UIView* view in self.view.subviews) {
// HACK: The official safe area is much too large for our purposes
// so we'll just use the presence of any safe area to indicate we should
// pad by 20.
if (self.view.safeAreaInsets.left >= 20 || self.view.safeAreaInsets.right >= 20) {
view.frame = CGRectMake(view.frame.origin.x + 20, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
}
if (@available(iOS 11.0, *)) {
for (UIView* view in self.view.subviews) {
// HACK: The official safe area is much too large for our purposes
// so we'll just use the presence of any safe area to indicate we should
// pad by 20.
if (self.view.safeAreaInsets.left >= 20 || self.view.safeAreaInsets.right >= 20) {
view.frame = CGRectMake(view.frame.origin.x + 20, view.frame.origin.y, view.frame.size.width, view.frame.size.height);
}
}
_adjustedForSafeArea = true;
}
}
- (void)viewDidLoad {
[super viewDidLoad];
// Always run settings in dark mode because we want the light fonts
if (@available(iOS 13.0, tvOS 13.0, *)) {
self.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
}
DataManager* dataMan = [[DataManager alloc] init];
TemporarySettings* currentSettings = [dataMan getSettings];
@@ -134,11 +145,14 @@ static const int bitrateTable[] = {
case 1080:
resolution = 2;
break;
case 2160:
resolution = 3;
break;
}
// Only show the 120 FPS option if we have a > 60-ish Hz display
bool enable120Fps = false;
if (@available(iOS 10.3, *)) {
if (@available(iOS 10.3, tvOS 10.3, *)) {
if ([UIScreen mainScreen].maximumFramesPerSecond > 62) {
enable120Fps = true;
}
@@ -147,10 +161,39 @@ static const int bitrateTable[] = {
[self.framerateSelector removeSegmentAtIndex:2 animated:NO];
}
// Only show the 4K option for "recent" devices. We'll judge that by whether
// they support HEVC decoding (A9 or later).
if (@available(iOS 11.0, tvOS 11.0, *)) {
if (!VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)) {
[self.resolutionSelector removeSegmentAtIndex:3 animated:NO];
}
}
else {
[self.resolutionSelector removeSegmentAtIndex:3 animated:NO];
}
// Disable the HEVC selector if HEVC is not supported by the hardware
// or the version of iOS. See comment in Connection.m for reasoning behind
// the iOS 11.3 check.
if (@available(iOS 11.3, tvOS 11.3, *)) {
if (VTIsHardwareDecodeSupported(kCMVideoCodecType_HEVC)) {
[self.hevcSelector setSelectedSegmentIndex:currentSettings.useHevc ? 1 : 0];
}
else {
[self.hevcSelector removeAllSegments];
[self.hevcSelector insertSegmentWithTitle:@"Unsupported on this device" atIndex:0 animated:NO];
[self.hevcSelector setEnabled:NO];
}
}
else {
[self.hevcSelector removeAllSegments];
[self.hevcSelector insertSegmentWithTitle:@"Requires iOS 11.3 or later" atIndex:0 animated:NO];
[self.hevcSelector setEnabled:NO];
}
[self.optimizeSettingsSelector setSelectedSegmentIndex:currentSettings.optimizeGames ? 1 : 0];
[self.multiControllerSelector setSelectedSegmentIndex:currentSettings.multiController ? 1 : 0];
[self.audioOnPCSelector setSelectedSegmentIndex:currentSettings.playAudioOnPC ? 1 : 0];
[self.hevcSelector setSelectedSegmentIndex:currentSettings.useHevc ? 1 : 0];
NSInteger onscreenControls = [currentSettings.onscreenControls integerValue];
[self.resolutionSelector setSelectedSegmentIndex:resolution];
[self.resolutionSelector addTarget:self action:@selector(newResolutionFpsChosen) forControlEvents:UIControlEventValueChanged];
@@ -196,7 +239,7 @@ static const int bitrateTable[] = {
}
// We should always be exactly on a slider position with default bitrates
_bitrate = defaultBitrate;
_bitrate = MIN(defaultBitrate, 100000);
assert(bitrateTable[[self getSliderValueForBitrate:_bitrate]] == _bitrate);
[self.bitrateSlider setValue:[self getSliderValueForBitrate:_bitrate] animated:YES];
@@ -228,7 +271,7 @@ static const int bitrateTable[] = {
}
- (NSInteger) getChosenStreamHeight {
const int resolutionTable[] = { 360, 720, 1080 };
const int resolutionTable[] = { 360, 720, 1080, 2160 };
return resolutionTable[[self.resolutionSelector selectedSegmentIndex]];
}
@@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <Limelight.h>
@implementation StreamFrameViewController {
ControllerSupport *_controllerSupport;
@@ -22,6 +23,7 @@
NSTimer *_inactivityTimer;
UITapGestureRecognizer *_menuGestureRecognizer;
UITapGestureRecognizer *_menuDoubleTapGestureRecognizer;
UITextView *_overlayView;
}
- (void)viewDidAppear:(BOOL)animated
@@ -48,6 +50,8 @@
[self.navigationController setNavigationBarHidden:YES animated:YES];
[(StreamView*)self.view setupStreamView];
[self.stageLabel setText:[NSString stringWithFormat:@"Starting %@...", self.streamConfig.appName]];
[self.stageLabel sizeToFit];
self.stageLabel.textAlignment = NSTextAlignmentCenter;
@@ -118,6 +122,34 @@
}
}
- (void)updateOverlayText:(NSString*)text {
if (_overlayView == nil) {
_overlayView = [[UITextView alloc] init];
#ifndef TARGET_OS_TV
[_overlayView setEditable:NO];
#endif
[_overlayView setUserInteractionEnabled:NO];
[_overlayView setSelectable:NO];
[_overlayView setScrollEnabled:NO];
[_overlayView setTextAlignment:NSTextAlignmentCenter];
[_overlayView setTextColor:[OSColor lightGrayColor]];
[_overlayView setBackgroundColor:[OSColor blackColor]];
[_overlayView setFont:[UIFont systemFontOfSize:12]];
[_overlayView setAlpha:0.5];
[self.view addSubview:_overlayView];
}
if (text != nil) {
[_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];
}
}
- (void) returnToMainFrame {
[self.navigationController popToRootViewControllerAnimated:YES];
}
@@ -264,6 +296,27 @@
[_controllerSupport rumble:controllerNumber lowFreqMotor:lowFreqMotor highFreqMotor:highFreqMotor];
}
- (void)connectionStatusUpdate:(int)status {
Log(LOG_W, @"Connection status update: %d", status);
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;
}
});
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
+7 -9
View File
@@ -1,11 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="14313.18" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pui-7y-JNH">
<device id="appleTV" orientation="landscape">
<adaptation id="light"/>
</device>
<document type="com.apple.InterfaceBuilder.AppleTV.Storyboard" version="3.0" toolsVersion="14868" targetRuntime="AppleTV" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pui-7y-JNH">
<device id="appleTV" appearance="dark"/>
<dependencies>
<deployment identifier="tvOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14283.14"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
@@ -15,10 +13,10 @@
<objects>
<navigationController id="pui-7y-JNH" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" translucent="NO" id="Lgd-OS-TqA">
<rect key="frame" x="0.0" y="0.0" width="1920" height="145"/>
<rect key="frame" x="0.0" y="60" width="1920" height="145"/>
<autoresizingMask key="autoresizingMask"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="barTintColor" red="0.20000000000000001" green="0.20000000000000001" blue="0.20000000000000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<textAttributes key="titleTextAttributes">
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<color key="textShadowColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -37,7 +35,7 @@
<objects>
<collectionViewController id="vKn-MI-uG7" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" prefetchingEnabled="YES" id="8P0-wP-bDe">
<rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
<rect key="frame" x="0.0" y="0.0" width="1920" height="875"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="50" minimumInteritemSpacing="50" id="m6l-TM-5FZ">
<size key="itemSize" width="300" height="400"/>
@@ -105,7 +103,7 @@
<viewControllerLayoutGuide type="bottom" id="6Xc-C7-Or1"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="CAm-nS-IFz" userLabel="RenderView" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="1920" height="1080"/>
<rect key="frame" x="0.0" y="0.0" width="1920" height="875"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="0eJ-kd-ugl" userLabel="Spinner">
+1 -1
View File
@@ -42,7 +42,7 @@
<string>arm64</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Automatic</string>
<string>Dark</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
+57 -61
View File
@@ -6,66 +6,6 @@
<string>Root</string>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Host Configuration</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>DefaultValue</key>
<false/>
<key>Title</key>
<string>Play Audio on Host PC</string>
<key>Key</key>
<string>audioOnPC</string>
</dict>
<dict>
<key>TrueValue</key>
<string>Optimize Games</string>
<key>FalseValue</key>
<string>Off</string>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Optimize Games</string>
<key>Key</key>
<string>optimizeGames</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>HEVC Video</string>
<key>Key</key>
<string>useHevc</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Enable HDR</string>
<key>Key</key>
<string>enableHdr</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Enable multi-controller support</string>
<key>Key</key>
<string>multipleControllers</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
@@ -122,7 +62,7 @@
<key>Key</key>
<string>bitrate</string>
<key>DefaultValue</key>
<string>10000</string>
<string>20000</string>
<key>Titles</key>
<array>
<string>0.5 Mbps</string>
@@ -148,6 +88,62 @@
<string>100000</string>
</array>
</dict>
<dict>
<key>Type</key>
<string>PSGroupSpecifier</string>
<key>Title</key>
<string>Host Configuration</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>DefaultValue</key>
<false/>
<key>Title</key>
<string>Play Audio on Host PC</string>
<key>Key</key>
<string>audioOnPC</string>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Optimize Game Settings</string>
<key>Key</key>
<string>optimizeGames</string>
<key>DefaultValue</key>
<true/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Use HEVC Codec</string>
<key>Key</key>
<string>useHevc</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Request HDR Video</string>
<key>Key</key>
<string>enableHdr</string>
<key>DefaultValue</key>
<false/>
</dict>
<dict>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>Title</key>
<string>Automatic Game Controller Detection</string>
<key>Key</key>
<string>multipleControllers</string>
<key>DefaultValue</key>
<true/>
</dict>
</array>
</dict>
</plist>
+9 -147
View File
@@ -22,7 +22,6 @@
98CFB82F1CAD481B0048EF74 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98AB2E841CAD46840089BB98 /* libmoonlight-common.a */; };
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */; };
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */; };
D46A73AD1CBC7D090039F1EE /* ControllerUnitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D46A73AC1CBC7D090039F1EE /* ControllerUnitTests.swift */; };
DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = DC1F5A06206436B20037755F /* ConnectionHelper.m */; };
FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1A674C2131E65900507771 /* KeyboardSupport.m */; };
FB1A67602132419700507771 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = FB1A675E2132419700507771 /* Main.storyboard */; };
@@ -137,13 +136,6 @@
remoteGlobalIDString = FB290E2D19B37A4E004C83CF;
remoteInfo = "moonlight-common";
};
D46A73A51CBC7CB60039F1EE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = FB290CE619B2C406004C83CF /* Project object */;
proxyType = 1;
remoteGlobalIDString = FB290CED19B2C406004C83CF;
remoteInfo = Moonlight;
};
FB1A68142132509400507771 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 98AB2E7F1CAD46830089BB98 /* moonlight-common.xcodeproj */;
@@ -166,6 +158,7 @@
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = "<group>"; };
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.3.xcdatamodel"; sourceTree = "<group>"; };
98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.4.xcdatamodel"; sourceTree = "<group>"; };
9865DC3B2132922E0005B9B9 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.2.xcdatamodel"; sourceTree = "<group>"; };
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = "<group>"; };
@@ -177,10 +170,6 @@
98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryHost.m; path = Database/TemporaryHost.m; sourceTree = "<group>"; };
98D5856E1C0ED0E800F6CC00 /* TemporarySettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporarySettings.h; path = Database/TemporarySettings.h; sourceTree = "<group>"; };
98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporarySettings.m; path = Database/TemporarySettings.m; sourceTree = "<group>"; };
D46A73A01CBC7CB60039F1EE /* MoonlightUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MoonlightUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
D46A73A41CBC7CB60039F1EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D46A73AB1CBC7D080039F1EE /* MoonlightUnitTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "MoonlightUnitTests-Bridging-Header.h"; sourceTree = "<group>"; };
D46A73AC1CBC7D090039F1EE /* ControllerUnitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ControllerUnitTests.swift; path = Input/ControllerUnitTests.swift; sourceTree = "<group>"; };
D4746EEA1CBC740C006FB401 /* Moonlight-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "Moonlight-Bridging-Header.h"; path = "Input/Moonlight-Bridging-Header.h"; sourceTree = "<group>"; };
DC1F5A05206436B10037755F /* ConnectionHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConnectionHelper.h; sourceTree = "<group>"; };
DC1F5A06206436B20037755F /* ConnectionHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ConnectionHelper.m; sourceTree = "<group>"; };
@@ -369,13 +358,6 @@
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
D46A739D1CBC7CB60039F1EE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
FB1A67502132419700507771 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -419,24 +401,6 @@
name = Products;
sourceTree = "<group>";
};
D46A73A11CBC7CB60039F1EE /* MoonlightUnitTests */ = {
isa = PBXGroup;
children = (
D46A73AB1CBC7D080039F1EE /* MoonlightUnitTests-Bridging-Header.h */,
D46A73AA1CBC7CEF0039F1EE /* Input */,
D46A73A41CBC7CB60039F1EE /* Info.plist */,
);
path = MoonlightUnitTests;
sourceTree = "<group>";
};
D46A73AA1CBC7CEF0039F1EE /* Input */ = {
isa = PBXGroup;
children = (
D46A73AC1CBC7D090039F1EE /* ControllerUnitTests.swift */,
);
name = Input;
sourceTree = "<group>";
};
FB1A67542132419700507771 /* Moonlight TV */ = {
isa = PBXGroup;
children = (
@@ -454,7 +418,6 @@
children = (
98AB2E7F1CAD46830089BB98 /* moonlight-common.xcodeproj */,
FB290CF919B2C406004C83CF /* Moonlight */,
D46A73A11CBC7CB60039F1EE /* MoonlightUnitTests */,
FB1A67542132419700507771 /* Moonlight TV */,
FB290CF019B2C406004C83CF /* Frameworks */,
FB290CEF19B2C406004C83CF /* Products */,
@@ -465,7 +428,6 @@
isa = PBXGroup;
children = (
FB290CEE19B2C406004C83CF /* Moonlight.app */,
D46A73A01CBC7CB60039F1EE /* MoonlightUnitTests.xctest */,
FB1A67532132419700507771 /* Moonlight TV.app */,
);
name = Products;
@@ -835,24 +797,6 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
D46A739F1CBC7CB60039F1EE /* MoonlightUnitTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = D46A73A91CBC7CB60039F1EE /* Build configuration list for PBXNativeTarget "MoonlightUnitTests" */;
buildPhases = (
D46A739C1CBC7CB60039F1EE /* Sources */,
D46A739D1CBC7CB60039F1EE /* Frameworks */,
D46A739E1CBC7CB60039F1EE /* Resources */,
);
buildRules = (
);
dependencies = (
D46A73A61CBC7CB60039F1EE /* PBXTargetDependency */,
);
name = MoonlightUnitTests;
productName = MoonlightUnitTests;
productReference = D46A73A01CBC7CB60039F1EE /* MoonlightUnitTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
FB1A67522132419700507771 /* Moonlight TV */ = {
isa = PBXNativeTarget;
buildConfigurationList = FB1A67682132419A00507771 /* Build configuration list for PBXNativeTarget "Moonlight TV" */;
@@ -899,11 +843,6 @@
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Moonlight Game Streaming Project";
TargetAttributes = {
D46A739F1CBC7CB60039F1EE = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0920;
TestTargetID = FB290CED19B2C406004C83CF;
};
FB1A67522132419700507771 = {
CreatedOnToolsVersion = 9.4.1;
LastSwiftMigration = 1010;
@@ -922,10 +861,9 @@
};
buildConfigurationList = FB290CE919B2C406004C83CF /* Build configuration list for PBXProject "Moonlight" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@@ -941,7 +879,6 @@
projectRoot = "";
targets = (
FB290CED19B2C406004C83CF /* Moonlight */,
D46A739F1CBC7CB60039F1EE /* MoonlightUnitTests */,
FB1A67522132419700507771 /* Moonlight TV */,
);
};
@@ -965,13 +902,6 @@
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
D46A739E1CBC7CB60039F1EE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
FB1A67512132419700507771 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -998,14 +928,6 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
D46A739C1CBC7CB60039F1EE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D46A73AD1CBC7D090039F1EE /* ControllerUnitTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
FB1A674F2132419700507771 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1114,11 +1036,6 @@
name = "moonlight-common";
targetProxy = 98AB2E851CAD468B0089BB98 /* PBXContainerItemProxy */;
};
D46A73A61CBC7CB60039F1EE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = FB290CED19B2C406004C83CF /* Moonlight */;
targetProxy = D46A73A51CBC7CB60039F1EE /* PBXContainerItemProxy */;
};
FB1A68172132509800507771 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "moonlight-common-tv";
@@ -1146,56 +1063,6 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
D46A73A71CBC7CB60039F1EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = MoonlightUnitTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = "com.moonlight-stream.MoonlightUnitTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "MoonlightUnitTests/MoonlightUnitTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Moonlight.app/Moonlight";
};
name = Debug;
};
D46A73A81CBC7CB60039F1EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = MoonlightUnitTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "com.moonlight-stream.MoonlightUnitTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "MoonlightUnitTests/MoonlightUnitTests-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Moonlight.app/Moonlight";
};
name = Release;
};
FB1A67662132419A00507771 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1205,7 +1072,6 @@
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
@@ -1248,7 +1114,6 @@
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
@@ -1373,6 +1238,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -1396,6 +1262,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 3;
ENABLE_BITCODE = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
@@ -1413,6 +1280,7 @@
"$(PROJECT_DIR)/libs/opus/lib/iOS",
"$(PROJECT_DIR)/libs/openssl/lib/iOS",
);
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.moonlight-stream.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@@ -1433,6 +1301,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CURRENT_PROJECT_VERSION = 3;
ENABLE_BITCODE = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
@@ -1450,6 +1319,7 @@
"$(PROJECT_DIR)/libs/opus/lib/iOS",
"$(PROJECT_DIR)/libs/openssl/lib/iOS",
);
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.moonlight-stream.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
@@ -1466,15 +1336,6 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
D46A73A91CBC7CB60039F1EE /* Build configuration list for PBXNativeTarget "MoonlightUnitTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
D46A73A71CBC7CB60039F1EE /* Debug */,
D46A73A81CBC7CB60039F1EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
FB1A67682132419A00507771 /* Build configuration list for PBXNativeTarget "Moonlight TV" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -1508,6 +1369,7 @@
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */,
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */,
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */,
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */,
@@ -1517,7 +1379,7 @@
FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */,
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */,
);
currentVersion = 98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */;
currentVersion = 98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */;
path = Limelight.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
-24
View File
@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -1,192 +0,0 @@
//
// ControllerUnitTests.swift
// Moonlight
//
// Created by David Aghassi on 4/11/16.
// Copyright © 2016 Moonlight Stream. All rights reserved.
//
import XCTest
@testable import Moonlight
/**
Tests the `Controller.swift` class
*/
class ControllerUnitTests: XCTestCase {
var controllerUnderTest: Controller!
override func setUp() {
controllerUnderTest = Controller()
XCTAssertNotNil(controllerUnderTest, "controllerUnderTest is nil, and should not be for this test. See line \(#line) \n")
}
override func tearDown() {
resetAllValues()
}
// MARK: Helper Methods
/**
Sets all the values of `controllerUnderTest` to `0`
*/
func resetAllValues() {
controllerUnderTest.emulatingButtonFlags = CInt(0)
controllerUnderTest.lastButtonFlags = CInt(0)
controllerUnderTest.lastLeftStickX = CShort(0)
controllerUnderTest.lastLeftStickY = CShort(0)
controllerUnderTest.lastRightStickX = CShort(0)
controllerUnderTest.lastRightStickY = CShort(0)
controllerUnderTest.lastLeftTrigger = CChar(0)
controllerUnderTest.lastRightTrigger = CChar(0)
}
/**
Displays "*name* failed to set CInt correctly." Shows expected and actual, as well as failure line.
- parameter name: The property being tested
- parameter expected: The expected value for the property
- parameter actual: The actual value for the property
- returns: A string with the failure in it. Formatted to state actual, expected, and the failure line.
*/
func displayCIntFailure(name: String, expected: CInt, actual: CInt) -> String {
return "\(name) failed to set CInt correctly \n. Expected: \(expected)\n. Actual: \(actual) \n. See line \(#line) \n"
}
/**
Displays "*name* failed to set CShort correctly." Shows expected and actual, as well as failure line.
- parameter name: The property being tested
- parameter expected: The expected value for the property
- parameter actual: The actual value for the property
- returns: A string with the failure in it. Formatted to state actual, expected, and the failure line.
*/
func displayCShortFailure(name: String, expected: CShort, actual: CShort) -> String {
return "\(name) failed to set CShort correctly \n. Expected: \(expected)\n. Actual: \(actual) \n. See line \(#line) \n"
}
/**
Displays "*name* failed to set CCHar correctly." Shows expected and actual, as well as the failure line.
- parameter name: The property being tested
- parameter expected: The expected value for the property
- parameter actual: The actual value for the property
- returns: A string with the failure in it. Formatted to state actual, expected, and the failure line.
*/
func displayCCharFailure(name: String, expected: CChar, actual: CChar) -> String {
return "\(name) failed to set CChar correctly \n. Expected: \(expected)\n. Actual: \(actual) \n. See line \(#line) \n"
}
// MARK: Tests
/**
Asserts that the `emulatingButtonFlags` is of type `CInt` and can be set and gotten from properly
*/
func test_Assert_emulatingButtonFlags_Sets_To_CInt() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.emulatingButtonFlags) == CInt.self, "Expected emulatingButtonFlags to be of type CInt. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CInt(1)
controllerUnderTest.emulatingButtonFlags = expected
XCTAssertTrue(controllerUnderTest.emulatingButtonFlags == expected, displayCIntFailure(name: "emulatingButtonFlags", expected: expected, actual: controllerUnderTest.emulatingButtonFlags))
}
/**
Asserts that the `lastButtonFlags` is of type `CInt` and can be set and gotten from properly
*/
func test_Assert_lastButtonFlags_Sets_To_CInt() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastButtonFlags) == CInt.self, "Expected lastButtonFlags to be of type CInt. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CInt(1)
controllerUnderTest.lastButtonFlags = expected
XCTAssertTrue(controllerUnderTest.lastButtonFlags == expected, displayCIntFailure(name: "lastButtonFlags", expected: expected, actual: controllerUnderTest.lastButtonFlags))
}
/**
Asserts that the `lastLeftStickX` is of type `CShort` and can be set and gotten from properly
*/
func test_Assert_lastLeftStickX_Sets_To_CShort() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastLeftStickX) == CShort.self, "Expected lastLeftStickX to be of type CShort. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CShort(1)
controllerUnderTest.lastLeftStickX = expected
XCTAssertTrue(controllerUnderTest.lastLeftStickX == expected, displayCShortFailure(name: "lastLeftStickX", expected: expected, actual: controllerUnderTest.lastLeftStickX))
}
/**
Asserts that lastLeftStickY` is of type `CShort` and can be set and gotten from properly
*/
func test_Assert_lastLeftStickY_Sets_To_CShort() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastLeftStickY) == CShort.self, "Expected lastLeftStickY to be of type CShort. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CShort(1)
controllerUnderTest.lastLeftStickY = expected
XCTAssertTrue(controllerUnderTest.lastLeftStickY == expected, displayCShortFailure(name: "lastLeftStickY", expected: expected, actual: controllerUnderTest.lastLeftStickY))
}
/**
Asserts that the `lastRightStickX` is of type `CShort` and can be set and gotten from properly
*/
func test_Assert_lastRightStickX_SetsTo_CShort() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastRightStickX) == CShort.self, "Expected lastRightStickX to be of type CShort. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CShort(1)
controllerUnderTest.lastRightStickX = expected
XCTAssertTrue(controllerUnderTest.lastRightStickX == expected, displayCShortFailure(name: "lastRightStickX", expected: expected, actual: controllerUnderTest.lastRightStickX))
}
/**
Asserts that the `lastRightStickY` is of type `CShort` and can be set and gotten from properly
*/
func test_Assert_lastRightStickY_Sets_To_CShort() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastRightStickY) == CShort.self, "Expected lastRightStickY to be of type CShort. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CShort(1)
controllerUnderTest.lastRightStickY = expected
XCTAssertTrue(controllerUnderTest.lastRightStickY == expected, displayCShortFailure(name: "lastRightStickY", expected: expected, actual: controllerUnderTest.lastRightStickY))
}
/**
Asserts that the `lastLeftTrigger` is of type `CChar` and can be set and gotten from properly
*/
func test_Assert_lastLeftTrigger_Sets_To_CChar() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastLeftTrigger) == CChar.self, "Expected lastLeftTrigger to be of type CChar. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CChar(1)
controllerUnderTest.lastLeftTrigger = expected
XCTAssertTrue(controllerUnderTest.lastLeftTrigger == expected, displayCCharFailure(name: "lastLeftTrigger", expected: expected, actual: controllerUnderTest.lastLeftTrigger))
}
/**
Asserts that the `lastRightTrigger` is of type `CChar` and can be set and gotten from properly
*/
func test_Assert_lastRightTrigger_Sets_To_CChar() {
// Assert type hasn't changed
XCTAssertTrue(type(of: controllerUnderTest.lastRightTrigger) == CChar.self, "Expected lastRightTrigger to be of type CChar. See line \(#line) \n")
// Assert value is assigned properly.
let expected = CChar(1)
controllerUnderTest.lastRightTrigger = expected
XCTAssertTrue(controllerUnderTest.lastRightTrigger == expected, displayCCharFailure(name: "lastRightTrigger", expected: expected, actual: controllerUnderTest.lastRightTrigger))
}
}
@@ -1,4 +0,0 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
+2
View File
@@ -1,5 +1,7 @@
# Moonlight iOS
[![Travis CI Status](https://travis-ci.org/moonlight-stream/moonlight-ios.svg?branch=master)](https://travis-ci.org/moonlight-stream/moonlight-ios)
#### Now available on the [App Store](https://itunes.apple.com/us/app/moonlight-game-streaming/id1000551566?mt=8)
[Moonlight](https://moonlight-stream.org) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for iOS. Moonlight iOS allows you to stream your full collection of Steam games from
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

+68 -67
View File
@@ -1,28 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" colorMatched="YES" initialViewController="EVd-wq-ego">
<device id="ipad9_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="EVd-wq-ego">
<device id="ipad11_0rounded" orientation="landscape" layout="fullscreen" appearance="dark"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<customFonts key="customFonts">
<array key="Roboto-Regular.ttf">
<string>Roboto-Regular</string>
</array>
</customFonts>
<scenes>
<!--Main Frame View Controller-->
<scene sceneID="Me4-Nr-liz">
<objects>
<viewController id="wb7-af-jn8" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" indicatorStyle="black" dataMode="prototypes" id="TZj-Lc-M9d" customClass="AppCollectionView">
<rect key="frame" x="0.0" y="0.0" width="768" height="960"/>
<rect key="frame" x="0.0" y="0.0" width="1194" height="784"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="contentInset" minX="40" minY="20" maxX="40" maxY="20"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="20" id="f7l-kG-hJc">
<size key="itemSize" width="150" height="200"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
@@ -45,40 +37,32 @@
</connections>
</collectionView>
<navigationItem key="navigationItem" id="pSu-bl-gL9">
<barButtonItem key="leftBarButtonItem" style="done" id="mSL-ru-nRm">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Tiq-VS-Ua5">
<rect key="frame" x="20" y="0.0" width="109" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Roboto-Regular" family="Roboto" pointSize="16"/>
<state key="normal" title="Moonlight" image="Logo">
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="No Host Selected" id="KDy-JC-2sU">
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</barButtonItem>
<barButtonItem key="leftBarButtonItem" title="Settings" id="mSL-ru-nRm"/>
<barButtonItem key="rightBarButtonItem" id="KDy-JC-2sU"/>
</navigationItem>
<connections>
<outlet property="computerNameButton" destination="KDy-JC-2sU" id="Lkg-yz-GE6"/>
<outlet property="limelightLogoButton" destination="Tiq-VS-Ua5" id="cue-NW-Hty"/>
<outlet property="settingsButton" destination="mSL-ru-nRm" id="NRf-lv-vlh"/>
<outlet property="upButton" destination="KDy-JC-2sU" id="tXB-Rc-IvJ"/>
<segue destination="OIm-0n-i9v" kind="push" identifier="createStreamFrame" id="7gN-E7-Ips"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ZYl-Xu-QyD" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="779" y="295"/>
<point key="canvasLocation" x="608.59375" y="172.8515625"/>
</scene>
<!--Settings View Controller-->
<scene sceneID="tWo-uo-hHg">
<objects>
<viewController id="BsV-3c-455" customClass="SettingsViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="ef6-uF-5Hh"/>
<viewControllerLayoutGuide type="bottom" id="XvO-F3-j4u"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="WRy-3f-gEP">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pYZ-GR-EwO">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pYZ-GR-EwO">
<rect key="frame" x="16" y="20" width="82" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -86,17 +70,19 @@
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="ckc-Dm-8ex">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="ckc-Dm-8ex">
<rect key="frame" x="16" y="49" width="209" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="360p"/>
<segment title="720p"/>
<segment title="1080p"/>
<segment title="4K"/>
</segments>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Frame Rate" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Aiv-r8-9k2">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Frame Rate" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Aiv-r8-9k2">
<rect key="frame" x="16" y="85" width="88" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -104,7 +90,7 @@
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="lGK-vl-pdw">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="lGK-vl-pdw">
<rect key="frame" x="16" y="114" width="209" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
@@ -113,8 +99,9 @@
<segment title="120 FPS"/>
</segments>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Bitrate: 10 Mbps" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lMt-4H-fkV">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Bitrate: 10 Mbps" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lMt-4H-fkV">
<rect key="frame" x="16" y="150" width="151" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -122,12 +109,12 @@
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="20" minValue="1" maxValue="100" id="JAY-nj-UNz">
<slider opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="20" minValue="1" maxValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="JAY-nj-UNz">
<rect key="frame" x="14" y="179" width="213" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="On-Screen Controls" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="LGV-5y-piG">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="On-Screen Controls" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="LGV-5y-piG">
<rect key="frame" x="16" y="217" width="153" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -135,7 +122,7 @@
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="qSU-wh-tqA">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="qSU-wh-tqA">
<rect key="frame" x="16" y="246" width="209" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
@@ -145,70 +132,75 @@
<segment title="Full"/>
</segments>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Optimize Game Settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="WSI-9V-G6c">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Optimize Game Settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WSI-9V-G6c">
<rect key="frame" x="16" y="282" width="186" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="nCO-OT-dV1" userLabel="Optimize Settings Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="nCO-OT-dV1" userLabel="Optimize Settings Selector">
<rect key="frame" x="16" y="311" width="209" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="No"/>
<segment title="Yes"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Multi-Controller Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="bFD-Ng-abn">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Multi-Controller Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="bFD-Ng-abn">
<rect key="frame" x="16" y="347" width="169" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="KlC-fG-wEi" userLabel="MC Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="KlC-fG-wEi" userLabel="MC Selector">
<rect key="frame" x="16" y="376" width="209" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Single"/>
<segment title="Auto"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Play Audio on PC" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="fXq-oD-MrL">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Play Audio on PC" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fXq-oD-MrL">
<rect key="frame" x="16" y="412" width="131" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="WDY-9o-6Hb" userLabel="PC Audio Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="WDY-9o-6Hb" userLabel="PC Audio Selector">
<rect key="frame" x="16" y="441" width="209" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="No"/>
<segment title="Yes"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="HEVC Video" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6w5-Md-NpV">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HEVC Video" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6w5-Md-NpV">
<rect key="frame" x="16" y="477" width="93" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="aFy-0w-YPe" userLabel="HEVC Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="aFy-0w-YPe" userLabel="HEVC Selector">
<rect key="frame" x="16" y="506" width="209" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="No"/>
<segment title="Yes"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="viewFlipsideBackgroundColor"/>
@@ -227,14 +219,18 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="01j-TU-OoL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="779" y="1425"/>
<point key="canvasLocation" x="608.59375" y="834.9609375"/>
</scene>
<!--Reveal View Controller-->
<scene sceneID="rR7-ZT-bc7">
<objects>
<viewController id="EVd-wq-ego" customClass="SWRevealViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="CpC-Tn-whm"/>
<viewControllerLayoutGuide type="bottom" id="YRO-Lg-RCg"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="hGs-BR-t4b">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
@@ -245,14 +241,14 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="lUU-kb-wrO" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-1728" y="514"/>
<point key="canvasLocation" x="-1350" y="301.171875"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="FxL-or-HET">
<objects>
<navigationController id="baW-rW-rBd" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" translucent="NO" id="RUe-14-4Ya">
<rect key="frame" x="0.0" y="20" width="768" height="44"/>
<rect key="frame" x="0.0" y="0.0" width="1194" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</navigationBar>
@@ -262,28 +258,32 @@
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iLe-Yz-zIG" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-397" y="514"/>
<point key="canvasLocation" x="-310.15625" y="301.171875"/>
</scene>
<!--Stream Frame View Controller-->
<scene sceneID="RuD-qk-7nb">
<objects>
<viewController storyboardIdentifier="MainFrame" id="OIm-0n-i9v" customClass="StreamFrameViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="l3k-pD-Q9i"/>
<viewControllerLayoutGuide type="bottom" id="vu8-3s-HlY"/>
</layoutGuides>
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="VPm-Ae-rc4" userLabel="RenderView" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<activityIndicatorView opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" id="iOs-1X-mSU">
<activityIndicatorView opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" hidesWhenStopped="YES" animating="YES" style="white" translatesAutoresizingMaskIntoConstraints="NO" id="iOs-1X-mSU">
<rect key="frame" x="502" y="374" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dDs-kT-po6">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="dDs-kT-po6">
<rect key="frame" x="491" y="402" width="42" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<textField hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" id="JkF-l4-Qyt">
<textField hidden="YES" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="JkF-l4-Qyt">
<rect key="frame" x="335" y="503" width="97" height="65"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -292,7 +292,7 @@
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardAppearance="alert" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tip Text" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Vij-AB-rV8" userLabel="Tip Label">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tip Text" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vij-AB-rV8" userLabel="Tip Label">
<rect key="frame" x="478" y="718" width="68" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
@@ -317,17 +317,21 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="hON-k2-Efa" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1959" y="295"/>
<point key="canvasLocation" x="1530.46875" y="172.8515625"/>
</scene>
<!--Loading Frame View Controller-->
<scene sceneID="lcG-48-utf">
<objects>
<viewController storyboardIdentifier="loadingFrame" modalTransitionStyle="crossDissolve" modalPresentationStyle="overCurrentContext" id="ZNo-L5-uxh" customClass="LoadingFrameViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="cVC-VG-qBz"/>
<viewControllerLayoutGuide type="bottom" id="YZj-An-sqS"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="le1-tU-NEp">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="whiteLarge" id="N0D-dj-EuF">
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="N0D-dj-EuF">
<rect key="frame" x="494" y="365" width="37" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
@@ -340,10 +344,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="lJG-tq-Jrs" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1959" y="1425"/>
<point key="canvasLocation" x="1530.46875" y="834.9609375"/>
</scene>
</scenes>
<resources>
<image name="Logo" width="32" height="32"/>
</resources>
</document>
+74 -72
View File
@@ -1,29 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES" initialViewController="DL0-L5-LOv">
<device id="retina4_7" orientation="landscape">
<adaptation id="fullscreen"/>
</device>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES" initialViewController="DL0-L5-LOv">
<device id="retina6_1" orientation="landscape" appearance="dark"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14824"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<customFonts key="customFonts">
<array key="Roboto-Regular.ttf">
<string>Roboto-Regular</string>
</array>
</customFonts>
<scenes>
<!--Main Frame View Controller-->
<scene sceneID="emz-kO-L7q">
<objects>
<viewController id="dgh-JZ-Q7z" customClass="MainFrameViewController" sceneMemberID="viewController">
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="Rtu-AT-Alw" customClass="AppCollectionView">
<rect key="frame" x="0.0" y="0.0" width="667" height="343"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="370"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.3333333432674408" green="0.3333333432674408" blue="0.3333333432674408" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<inset key="contentInset" minX="40" minY="20" maxX="40" maxY="20"/>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="20" id="YcZ-cR-3tK">
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="15" minimumInteritemSpacing="15" id="YcZ-cR-3tK">
<size key="itemSize" width="90" height="120"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
@@ -45,38 +37,30 @@
</connections>
</collectionView>
<toolbarItems/>
<navigationItem key="navigationItem" id="1jn-Sf-Xky">
<barButtonItem key="leftBarButtonItem" style="done" id="zAn-CM-7Yz">
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="pZ9-ft-T24">
<rect key="frame" x="20" y="0.0" width="109" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Roboto-Regular" family="Roboto" pointSize="16"/>
<state key="normal" title="Moonlight" image="Logo">
<color key="titleColor" red="0.9513210654258728" green="0.97490358352661133" blue="0.99987185001373291" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</state>
</button>
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="No Host Selected" id="L1H-n4-HB7">
<color key="tintColor" red="0.9513210654258728" green="0.97490358352661133" blue="0.99987185001373291" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</barButtonItem>
<navigationItem key="navigationItem" id="pSu-bl-gL9">
<barButtonItem key="leftBarButtonItem" title="Settings" id="mSL-ru-nRm"/>
<barButtonItem key="rightBarButtonItem" id="KDy-JC-2sU"/>
</navigationItem>
<connections>
<outlet property="computerNameButton" destination="L1H-n4-HB7" id="bng-Ky-oQs"/>
<outlet property="limelightLogoButton" destination="pZ9-ft-T24" id="yRw-rK-9lQ"/>
<segue destination="mI3-9F-XwU" kind="push" identifier="createStreamFrame" id="Vgc-B6-lvM"/>
<outlet property="settingsButton" destination="mSL-ru-nRm" id="NRf-lv-vlh"/>
<outlet property="upButton" destination="KDy-JC-2sU" id="tXB-Rc-IvJ"/>
<segue destination="mI3-9F-XwU" kind="push" identifier="createStreamFrame" id="NuQ-Ez-IEX"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="HNf-lX-GEO" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1362.5" y="795.5"/>
<point key="canvasLocation" x="1974.6376811594205" y="532.70089285714289"/>
</scene>
<!--Reveal View Controller-->
<scene sceneID="QYt-XN-Ojr">
<objects>
<viewController id="DL0-L5-LOv" customClass="SWRevealViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="r4Q-3y-YAU"/>
<viewControllerLayoutGuide type="bottom" id="iBq-4E-Dbr"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Usg-e0-g4a">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
@@ -87,41 +71,47 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="RXm-iV-Zqb" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-66" y="796"/>
<point key="canvasLocation" x="-95.652173913043484" y="533.03571428571422"/>
</scene>
<!--Side Bar-->
<scene sceneID="uYM-56-Ivb">
<objects>
<viewController id="rYd-e6-cQU" userLabel="Side Bar" customClass="SettingsViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="TeT-8j-xwM"/>
<viewControllerLayoutGuide type="bottom" id="HaZ-6S-ZpN"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="iNk-qF-gIr" customClass="UIScrollView">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8zy-ri-Dqc">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8zy-ri-Dqc">
<rect key="frame" x="16" y="20" width="82" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.9513210654258728" green="0.97490358352661133" blue="0.99987185001373291" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="PCM-t4-Sha">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="PCM-t4-Sha">
<rect key="frame" x="16" y="49" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="360p"/>
<segment title="720p"/>
<segment title="1080p"/>
<segment title="4K"/>
</segments>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Frame Rate" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Kkv-DF-MAl">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Frame Rate" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Kkv-DF-MAl">
<rect key="frame" x="16" y="85" width="88" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.9513210654258728" green="0.97490358352661133" blue="0.99987185001373291" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="dLF-qJ-2nY">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="dLF-qJ-2nY">
<rect key="frame" x="16" y="114" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
@@ -130,27 +120,28 @@
<segment title="120 FPS"/>
</segments>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Bitrate: 10 Mbps" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="SBv-Wn-LB7">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Bitrate: 10 Mbps" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SBv-Wn-LB7">
<rect key="frame" x="16" y="150" width="151" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.9513210654258728" green="0.97490358352661133" blue="0.99987185001373291" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="20" minValue="1" maxValue="100" id="3nn-MI-9Xu">
<slider opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="20" minValue="1" maxValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="3nn-MI-9Xu">
<rect key="frame" x="14" y="179" width="204" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="On-Screen Controls" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="HZT-dd-DuQ">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="On-Screen Controls" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HZT-dd-DuQ">
<rect key="frame" x="16" y="217" width="153" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.9513210654258728" green="0.97490358352661133" blue="0.99987185001373291" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="WGf-9d-eAm">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="WGf-9d-eAm">
<rect key="frame" x="16" y="246" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
@@ -160,70 +151,75 @@
<segment title="Full"/>
</segments>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Optimize Game Settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hxa-gT-JXC">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Optimize Game Settings" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hxa-gT-JXC">
<rect key="frame" x="16" y="282" width="186" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="Gob-Lu-b1y" userLabel="Optimize Settings Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="Gob-Lu-b1y" userLabel="Optimize Settings Selector">
<rect key="frame" x="16" y="311" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="No"/>
<segment title="Yes"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Multi-Controller Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Jps-kj-bYJ">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Multi-Controller Mode" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jps-kj-bYJ">
<rect key="frame" x="16" y="347" width="169" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="OCT-oL-Dqb" userLabel="MC Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" translatesAutoresizingMaskIntoConstraints="NO" id="OCT-oL-Dqb" userLabel="MC Selector">
<rect key="frame" x="16" y="376" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="Single"/>
<segment title="Auto"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Play Audio on PC" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="T6M-nz-TW0">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Play Audio on PC" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="T6M-nz-TW0">
<rect key="frame" x="16" y="412" width="131" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="8GA-Js-kQN" userLabel="PC Audio Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="8GA-Js-kQN" userLabel="PC Audio Selector">
<rect key="frame" x="16" y="441" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="No"/>
<segment title="Yes"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="HEVC Video" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="BEG-Id-J8y">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="HEVC Video" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BEG-Id-J8y">
<rect key="frame" x="16" y="477" width="93" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.93902439019999995" green="0.9625305918" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<segmentedControl opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="AbS-CW-fjP" userLabel="HEVC Selector">
<segmentedControl opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="AbS-CW-fjP" userLabel="HEVC Selector">
<rect key="frame" x="16" y="506" width="200" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<segments>
<segment title="No"/>
<segment title="Yes"/>
</segments>
<color key="tintColor" red="0.6716768742" green="0.61711704730000005" blue="0.99902987480000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="tintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="selectedSegmentTintColor" red="0.6716768741607666" green="0.61711704730987549" blue="0.99902987480163574" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</segmentedControl>
</subviews>
<color key="backgroundColor" red="0.12156862745098039" green="0.12941176470588237" blue="0.14117647058823529" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@@ -243,14 +239,14 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="RWc-Km-KG5" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1362.5" y="1265.5"/>
<point key="canvasLocation" x="1974" y="1487"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="pfX-8A-htT">
<objects>
<navigationController id="ftZ-kC-fxI" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" translucent="NO" id="0ZA-Ec-QgD">
<rect key="frame" x="0.0" y="0.0" width="667" height="32"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<color key="barTintColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes">
@@ -263,28 +259,32 @@
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Dr2-S8-dHh" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="660" y="796"/>
<point key="canvasLocation" x="956.52173913043487" y="533.03571428571422"/>
</scene>
<!--Stream Frame View Controller-->
<scene sceneID="5Eb-q2-vjt">
<objects>
<viewController id="mI3-9F-XwU" customClass="StreamFrameViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="JIX-qR-Sh3"/>
<viewControllerLayoutGuide type="bottom" id="ody-Fj-H4p"/>
</layoutGuides>
<view key="view" multipleTouchEnabled="YES" contentMode="scaleToFill" id="eir-e9-IPE" customClass="StreamView">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2HK-Z5-4Ch">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Stage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2HK-Z5-4Ch">
<rect key="frame" x="262" y="149" width="45" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" id="0vm-Iv-K4b">
<rect key="frame" x="324" y="143" width="20" height="20"/>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" hidesWhenStopped="YES" animating="YES" style="white" translatesAutoresizingMaskIntoConstraints="NO" id="0vm-Iv-K4b">
<rect key="frame" x="439" y="159" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
<textField hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" id="r45-6t-nxS">
<textField hidden="YES" opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="r45-6t-nxS">
<rect key="frame" x="406" y="293" width="97" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="1" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
@@ -293,7 +293,7 @@
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardAppearance="alert" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
</textField>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tip Text" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QwC-QD-dae" userLabel="Tip Label">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Tip Text" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="QwC-QD-dae" userLabel="Tip Label">
<rect key="frame" x="304" y="334" width="61" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@@ -318,18 +318,23 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="pqv-jd-33O" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2092" y="796"/>
<point key="canvasLocation" x="3031.884057971015" y="533.03571428571422"/>
</scene>
<!--Loading Frame View Controller-->
<scene sceneID="kPF-XD-S77">
<objects>
<viewController storyboardIdentifier="loadingFrame" modalTransitionStyle="crossDissolve" modalPresentationStyle="overCurrentContext" id="9W0-2D-0Kp" customClass="LoadingFrameViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="VID-dc-yvh"/>
<viewControllerLayoutGuide type="bottom" id="hby-2y-jAk"/>
</layoutGuides>
<view key="view" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="center" id="nSw-dc-mi4">
<rect key="frame" x="0.0" y="0.0" width="667" height="375"/>
<rect key="frame" x="0.0" y="0.0" width="896" height="414"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="oNu-Gu-QeM">
<rect key="frame" x="266" y="168" width="37" height="37"/>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" animating="YES" style="whiteLarge" translatesAutoresizingMaskIntoConstraints="NO" id="oNu-Gu-QeM">
<rect key="frame" x="266" y="187" width="37" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMinY="YES" flexibleMaxY="YES"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.5" colorSpace="custom" customColorSpace="sRGB"/>
@@ -340,10 +345,7 @@
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="GaV-PK-JSI" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2104" y="1266"/>
<point key="canvasLocation" x="3031" y="1487"/>
</scene>
</scenes>
<resources>
<image name="Logo" width="32" height="32"/>
</resources>
</document>
@@ -365,10 +365,11 @@
};
buildConfigurationList = FB290E2919B37A4E004C83CF /* Build configuration list for PBXProject "moonlight-common" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = FB290E2519B37A4E004C83CF;
productRefGroup = FB290E2F19B37A4E004C83CF /* Products */;
@@ -543,7 +544,7 @@
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN_UNREACHABLE_CODE = NO;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "Mac Developer";
COPY_PHASE_STRIP = NO;
@@ -562,7 +563,7 @@
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
@@ -600,7 +601,7 @@
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN_UNREACHABLE_CODE = NO;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "3rd Party Mac Developer Application";
COPY_PHASE_STRIP = YES;
@@ -612,7 +613,7 @@
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = NO;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;