Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
61b6e25a91 | ||
|
|
50cff820de | ||
|
|
4e586a28c8 | ||
|
|
0702b4ed57 | ||
|
|
4a2132a682 | ||
|
|
ba81d1d6a9 | ||
|
|
402ce4b3ec | ||
|
|
fc713453ad | ||
|
|
b222ccffbb | ||
|
|
56ce7329d2 | ||
|
|
99ab6db557 | ||
|
|
d7adf96f3a | ||
|
|
0ae58c61a9 | ||
|
|
2ec7d1d2f1 | ||
|
|
ad1b9059ee | ||
|
|
4439c7cce6 | ||
|
|
b8a4093c75 | ||
|
|
ec3748b26e | ||
|
|
1c1f22e047 | ||
|
|
9aa9b0fc5a | ||
|
|
f8e2ca8500 | ||
|
|
3076e29335 | ||
|
|
fe616d86c5 | ||
|
|
bfa4856bbe | ||
|
|
5ea92a7b48 | ||
|
|
4ba71db7b2 | ||
|
|
c1e6aeb43c | ||
|
|
e3ffb66ef0 | ||
|
|
5daa82b0ba | ||
|
|
a8bcc6afba | ||
|
|
e6aa16ad01 | ||
|
|
ad0705d126 | ||
|
|
2f31f93bf8 | ||
|
|
ea3be613c5 | ||
|
|
b495246f34 | ||
|
|
814470f4bf | ||
|
|
82293a08b6 | ||
|
|
20d66336a7 | ||
|
|
c8441ec9fa | ||
|
|
f72135c227 | ||
|
|
100e6e4ce8 | ||
|
|
ec8be20d62 | ||
|
|
55d48101ef | ||
|
|
c36f4c5f84 | ||
|
|
06289ac4d2 | ||
|
|
e6a2d5d425 | ||
|
|
ea8db9c4ea | ||
|
|
3773e841d1 | ||
|
|
ad1ddd04f3 | ||
|
|
67a8b232b9 | ||
|
|
5293c07fc0 | ||
|
|
e3a42db007 | ||
|
|
3b1462af22 | ||
|
|
74a0c5b1cd | ||
|
|
05a5e4fa64 | ||
|
|
b6445295a7 | ||
|
|
391605ef36 | ||
|
|
695499dea9 | ||
|
|
9ba7f35444 | ||
|
|
37dc00735b | ||
|
|
920cbadc91 | ||
|
|
bc53b4dc32 | ||
|
|
6015c38d17 | ||
|
|
4ea8694a7d | ||
|
|
00f01196fb | ||
|
|
543639c93e | ||
|
|
1da877fed7 | ||
|
|
396b5fc156 | ||
|
|
297dfd4159 | ||
|
|
23e9d48b0d | ||
|
|
9ec650a84f | ||
|
|
90bdbc4ae0 | ||
|
|
dd5f06723b | ||
|
|
55b2871b83 | ||
|
|
c8b13126d0 | ||
|
|
96cfe0b70d | ||
|
|
6dbb938368 | ||
|
|
46fe7a8aed | ||
|
|
3151ac6bf0 | ||
|
|
dc5210f691 | ||
|
|
1f02265003 | ||
|
|
e4af9032e2 | ||
|
|
e8650b2420 | ||
|
|
9c6b718be2 | ||
|
|
8f12114a56 | ||
|
|
dc6ebffab1 | ||
|
|
13e894e9a3 | ||
|
|
4c95773131 | ||
|
|
38776a01e4 | ||
|
|
b2563e534d | ||
|
|
37f25c1f97 | ||
|
|
642085ca32 | ||
|
|
5dee29a21c | ||
|
|
b01d13976b | ||
|
|
ae1c83a011 | ||
|
|
e5f962dc3f | ||
|
|
54dd79d2dd | ||
|
|
935fb6ffe3 | ||
|
|
48d1c1a080 | ||
|
|
2d02ce2a1b | ||
|
|
29fda59a72 | ||
|
|
05f0dfd4f5 | ||
|
|
0bdc5a83f1 | ||
|
|
8688bf1210 | ||
|
|
bf0858d57e | ||
|
|
8d4d391310 | ||
|
|
8fb15336c4 | ||
|
|
883385c42b | ||
|
|
6d1139796e | ||
|
|
e58eb5f56a | ||
|
|
2b0ce49646 | ||
|
|
73364127d2 | ||
|
|
84a986bc88 | ||
|
|
f13051aa31 | ||
|
|
6bb559303b | ||
|
|
b7324f9a44 | ||
|
|
c0b2bbe552 | ||
|
|
8c36cf40fd | ||
|
|
717296269f | ||
|
|
4be29abede | ||
|
|
528af9684e | ||
|
|
ff7a4ef190 | ||
|
|
99a2dbb956 | ||
|
|
280341791a | ||
|
|
1759565920 | ||
|
|
f57fa678f6 | ||
|
|
f8b5b16109 | ||
|
|
005a678089 | ||
|
|
315dd53dcb | ||
|
|
bb24495b3f | ||
|
|
b9f2764990 | ||
|
|
a9a00629b1 | ||
|
|
1223a9013f | ||
|
|
ab019eef0f | ||
|
|
20f82ff739 | ||
|
|
4d55a6a7e9 | ||
|
|
d7ec41d56e | ||
|
|
a6a1929a43 | ||
|
|
722850ceb6 | ||
|
|
5a976764c3 | ||
|
|
f90becb2f6 | ||
|
|
5296b2eb39 | ||
|
|
987a30e15f | ||
|
|
0880c3639c | ||
|
|
2972d40d92 | ||
|
|
c4430ef18d | ||
|
|
6b97af13bf | ||
|
|
cd18ca64bb | ||
|
|
d3c6302681 | ||
|
|
58bd3cc706 | ||
|
|
8d3401f962 | ||
|
|
dfc86015bd | ||
|
|
772ee3a680 | ||
|
|
f217ca9bbe | ||
|
|
fab8d604d2 | ||
|
|
7767ae497b | ||
|
|
58e41dfcf1 | ||
|
|
796ab2bf90 | ||
|
|
dfa07bf94b | ||
|
|
f43c74d59b | ||
|
|
fd9ee45dac | ||
|
|
40a04eb08b | ||
|
|
e9cff8f7b7 | ||
|
|
acee0af9df | ||
|
|
8d3e81ed25 | ||
|
|
5f8b0672f7 | ||
|
|
62bbec3c5d | ||
|
|
65f7e75736 | ||
|
|
3617ea98e2 | ||
|
|
81c84597c8 | ||
|
|
ea231e16b6 | ||
|
|
c205b99080 | ||
|
|
e3dd4e7238 | ||
|
|
0c36754ebe | ||
|
|
e7dc22bb15 | ||
|
|
d4b5ec5764 | ||
|
|
bed8c729f9 | ||
|
|
0a767767fb | ||
|
|
e31df3f44e | ||
|
|
9c9795ab1f | ||
|
|
c7c28a18c5 | ||
|
|
93119f25a6 | ||
|
|
1313429f65 | ||
|
|
11c27baaf8 | ||
|
|
d728e63bb4 | ||
|
|
7d584fb4de | ||
|
|
7d7adb0753 | ||
|
|
337e5e3d9d | ||
|
|
879f304a79 | ||
|
|
75d3f23f0a | ||
|
|
14f30349a3 | ||
|
|
8e721a9c6a | ||
|
|
e3e95c29f6 |
@@ -1,2 +1,3 @@
|
||||
**/xcuserdata/
|
||||
**/Moonlight.xcscmblueprint
|
||||
Build
|
||||
|
||||
@@ -31,43 +31,44 @@ do
|
||||
exit 1
|
||||
fi
|
||||
echo "Compiling source for $ARCH in directory $OPUS_DIR"
|
||||
|
||||
echo "cd $OPUS_DIR"
|
||||
cd $OPUS_DIR
|
||||
|
||||
DIST_DIR=$DIST_DIR_BASE-$ARCH
|
||||
echo "mkdir -p $DIST_DIR"
|
||||
mkdir -p $DIST_DIR
|
||||
CFLAGS_ARCH=$ARCH
|
||||
case $ARCH in
|
||||
armv7)
|
||||
EXTRA_FLAGS="--with-pic --enable-fixed-point"
|
||||
EXTRA_CFLAGS="-mcpu=cortex-a8 -mfpu=neon"
|
||||
EXTRA_CFLAGS="-mcpu=cortex-a8 -mfpu=neon -miphoneos-version-min=7.1"
|
||||
PLATFORM="${PLATFORMBASE}/iPhoneOS.platform"
|
||||
IOSSDK=iPhoneOS${IOSSDK_VER}
|
||||
IOSSDK=iPhoneOS
|
||||
;;
|
||||
armv7s)
|
||||
EXTRA_FLAGS="--with-pic --enable-fixed-point"
|
||||
EXTRA_CFLAGS="-mcpu=cortex-a9 -mfpu=neon -miphoneos-version-min=6.0"
|
||||
EXTRA_CFLAGS="-mcpu=cortex-a9 -mfpu=neon -miphoneos-version-min=7.1"
|
||||
PLATFORM="${PLATFORMBASE}/iPhoneOS.platform"
|
||||
IOSSDK=iPhoneOS${IOSSDK_VER}
|
||||
IOSSDK=iPhoneOS
|
||||
;;
|
||||
aarch64)
|
||||
CFLAGS_ARCH="arm64"
|
||||
EXTRA_FLAGS="--with-pic --enable-fixed-point"
|
||||
EXTRA_CFLAGS="-miphoneos-version-min=7.1"
|
||||
PLATFORM="${PLATFORMBASE}/iPhoneOS.platform"
|
||||
IOSSDK=iPhoneOS${IOSSDK_VER}
|
||||
IOSSDK=iPhoneOS
|
||||
;;
|
||||
x86_64)
|
||||
EXTRA_FLAGS="--with-pic"
|
||||
EXTRA_CFLAGS="-miphoneos-version-min=7.1"
|
||||
PLATFORM="${PLATFORMBASE}/iPhoneSimulator.platform"
|
||||
IOSSDK=iPhoneSimulator${IOSSDK_VER}
|
||||
IOSSDK=iPhoneSimulator
|
||||
;;
|
||||
i386)
|
||||
EXTRA_FLAGS="--with-pic"
|
||||
EXTRA_CFLAGS="-miphoneos-version-min=7.1"
|
||||
PLATFORM="${PLATFORMBASE}/iPhoneSimulator.platform"
|
||||
IOSSDK=iPhoneSimulator${IOSSDK_VER}
|
||||
IOSSDK=iPhoneSimulator
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported architecture ${ARCH}"
|
||||
@@ -76,7 +77,7 @@ do
|
||||
esac
|
||||
|
||||
echo "Configuring opus for $ARCH..."
|
||||
|
||||
echo "./autogen.sh"
|
||||
./autogen.sh
|
||||
|
||||
CFLAGS="-g -O2 -pipe -arch ${CFLAGS_ARCH} \
|
||||
@@ -86,7 +87,10 @@ do
|
||||
LDFLAGS="-arch ${CFLAGS_ARCH} \
|
||||
-isysroot ${PLATFORM}/Developer/SDKs/${IOSSDK}.sdk \
|
||||
-L${PLATFORM}/Developer/SDKs/${IOSSDK}.sdk/usr/lib"
|
||||
|
||||
|
||||
echo "CFLAGS=$CFLAGS"
|
||||
echo "LDFLAGS=$LDFLAGS"
|
||||
|
||||
export CFLAGS
|
||||
export LDFLAGS
|
||||
|
||||
@@ -100,7 +104,15 @@ do
|
||||
export NM="/usr/bin/nm"
|
||||
export RANLIB="/usr/bin/ranlib"
|
||||
export STRIP="/usr/bin/strip"
|
||||
|
||||
echo "./configure \
|
||||
--prefix=$DIST_DIR \
|
||||
--host=${ARCH}-apple-darwin \
|
||||
--with-sysroot=${PLATFORM}/Developer/SDKs/${IOSSDK}.sdk \
|
||||
--enable-static=yes \
|
||||
--enable-shared=no \
|
||||
--disable-doc \
|
||||
${EXTRA_FLAGS}"
|
||||
|
||||
./configure \
|
||||
--prefix=$DIST_DIR \
|
||||
--host=${ARCH}-apple-darwin \
|
||||
@@ -111,10 +123,14 @@ do
|
||||
${EXTRA_FLAGS}
|
||||
|
||||
echo "Installing opus for $ARCH..."
|
||||
echo "make clean"
|
||||
make clean
|
||||
echo "make -j$NJOB V=1"
|
||||
make -j$NJOB V=1
|
||||
echo "make install"
|
||||
make install
|
||||
|
||||
echo "cd $SCRIPT_DIR"
|
||||
cd $SCRIPT_DIR
|
||||
|
||||
if [ -d $DIST_DIR/bin ]
|
||||
|
||||
@@ -7,7 +7,6 @@ source config.sh
|
||||
SCRIPT_DIR=$( (cd -P $(dirname $0) && pwd) )
|
||||
DIST_DIR_BASE=${DIST_DIR_BASE:="$SCRIPT_DIR/dist"}
|
||||
|
||||
git submodule update --init opus
|
||||
cd opus
|
||||
git checkout master
|
||||
git pull origin master
|
||||
|
||||
@@ -1,41 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6254" systemVersion="14C81f" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
|
||||
<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">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="iN0-l3-epB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text=" Copyright (c) 2014 Limelight Stream. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
|
||||
<rect key="frame" x="22" y="439" width="437" height="21"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="0.070588235294117646" green="0.45490196078431372" blue="0.074509803921568626" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<variation key="widthClass=compact">
|
||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||
</variation>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Limelight Flat Design Banner" translatesAutoresizingMaskIntoConstraints="NO" id="lr8-1h-ydW">
|
||||
<rect key="frame" x="-8" y="115" width="496" height="251"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="centerY" secondItem="lr8-1h-ydW" secondAttribute="centerY" id="C8A-9E-cM6"/>
|
||||
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Gnz-uJ-CDw"/>
|
||||
<constraint firstAttribute="centerX" secondItem="lr8-1h-ydW" secondAttribute="centerX" id="WCv-Cd-TyR"/>
|
||||
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
|
||||
</constraints>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="404" y="445"/>
|
||||
</view>
|
||||
<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"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
<color key="barTintColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</navigationBar>
|
||||
<viewControllers>
|
||||
<viewController id="l6V-ue-5Om">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="rEZ-vx-akr"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="gBD-cy-cth"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="6lF-fc-kpK">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</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" lineBreakMode="middleTruncation" id="9JC-B5-BAE">
|
||||
<rect key="frame" x="-23" y="-15" width="46" height="30"/>
|
||||
<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>
|
||||
</viewController>
|
||||
</viewControllers>
|
||||
</navigationController>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Limelight Flat Design Banner" width="496" height="251"/>
|
||||
<image name="Logo" width="32" height="32"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
//
|
||||
// App.h
|
||||
// Limelight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/22/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface App : NSObject
|
||||
|
||||
@property NSString* appId;
|
||||
@property NSString* appName;
|
||||
@property UIImage* appImage;
|
||||
@property BOOL isRunning;
|
||||
|
||||
@end
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// App.m
|
||||
// Limelight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/22/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "App.h"
|
||||
#import "HttpManager.h"
|
||||
|
||||
@implementation App
|
||||
@synthesize appId, appName, appImage, isRunning;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// AppCollectionView.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface AppCollectionView : UICollectionView
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// AppCollectionView.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppCollectionView.h"
|
||||
|
||||
@implementation AppCollectionView
|
||||
|
||||
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
|
||||
if ([view isKindOfClass:[UIButton class]]) {
|
||||
return YES;
|
||||
}
|
||||
return [super touchesShouldCancelInContentView:view];
|
||||
}
|
||||
|
||||
/*
|
||||
// Only override drawRect: if you perform custom drawing.
|
||||
// An empty implementation adversely affects performance during animation.
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
// Drawing code
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// AppDelegate.h
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/17/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// AppDelegate.m
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/17/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
@@ -83,10 +83,7 @@ static NSOperationQueue* mainQueue;
|
||||
NSManagedObjectContext *managedObjectContext = self.managedObjectContext;
|
||||
if (managedObjectContext != nil) {
|
||||
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) {
|
||||
// Replace this implementation with code to handle the error appropriately.
|
||||
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
|
||||
abort();
|
||||
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +100,7 @@ static NSOperationQueue* mainQueue;
|
||||
|
||||
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
|
||||
if (coordinator != nil) {
|
||||
_managedObjectContext = [[NSManagedObjectContext alloc] init];
|
||||
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
|
||||
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
|
||||
}
|
||||
return _managedObjectContext;
|
||||
@@ -136,32 +133,15 @@ static NSOperationQueue* mainQueue;
|
||||
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
|
||||
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
|
||||
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) {
|
||||
/*
|
||||
Replace this implementation with code to handle the error appropriately.
|
||||
|
||||
abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
||||
|
||||
Typical reasons for an error here include:
|
||||
* The persistent store is not accessible;
|
||||
* The schema for the persistent store is incompatible with current managed object model.
|
||||
Check the error message to determine what the actual problem was.
|
||||
|
||||
|
||||
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
|
||||
|
||||
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
|
||||
* Simply deleting the existing store:
|
||||
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
|
||||
|
||||
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
|
||||
@{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES}
|
||||
|
||||
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
|
||||
|
||||
*/
|
||||
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
|
||||
abort();
|
||||
}
|
||||
// Log the error
|
||||
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
|
||||
|
||||
// Drop the database
|
||||
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil];
|
||||
|
||||
// Try again
|
||||
return [self persistentStoreCoordinator];
|
||||
}
|
||||
|
||||
return _persistentStoreCoordinator;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ComputerScrollView.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface ComputerScrollView : UIScrollView
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// ComputerScrollView.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ComputerScrollView.h"
|
||||
|
||||
@implementation ComputerScrollView
|
||||
|
||||
- (BOOL)touchesShouldCancelInContentView:(UIView *)view {
|
||||
if ([view isKindOfClass:[UIButton class]]) {
|
||||
return YES;
|
||||
}
|
||||
return [super touchesShouldCancelInContentView:view];
|
||||
}
|
||||
|
||||
/*
|
||||
// Only override drawRect: if you perform custom drawing.
|
||||
// An empty implementation adversely affects performance during animation.
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
// Drawing code
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// CryptoManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/14/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -11,11 +11,11 @@
|
||||
@interface CryptoManager : NSObject
|
||||
|
||||
+ (void) generateKeyPairUsingSSl;
|
||||
+ (NSString*) getUniqueID;
|
||||
+ (NSData*) readCertFromFile;
|
||||
+ (NSData*) readKeyFromFile;
|
||||
+ (NSData*) readP12FromFile;
|
||||
+ (NSData*) getSignatureFromCert:(NSData*)cert;
|
||||
+ (NSData*) nullTerminateString:(NSData*)data;
|
||||
|
||||
- (NSData*) createAESKeyFromSalt:(NSData*)saltedPIN;
|
||||
- (NSData*) SHA1HashData:(NSData*)data;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
//
|
||||
// CryptoManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/14/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CryptoManager.h"
|
||||
#import "mkcert.h"
|
||||
#import <AdSupport/ASIdentifierManager.h>
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/sha.h>
|
||||
@@ -76,8 +75,14 @@ static NSData* p12 = nil;
|
||||
return (((int)[data length] + 15) / 16) * 16;
|
||||
}
|
||||
|
||||
+ (NSData*) nullTerminateString:(NSData*)data {
|
||||
NSMutableData* mutData = [NSMutableData dataWithData:data];
|
||||
[mutData appendBytes:"" length:1];
|
||||
return mutData;
|
||||
}
|
||||
|
||||
- (bool) verifySignature:(NSData *)data withSignature:(NSData*)signature andCert:(NSData*)cert {
|
||||
const char* buffer = [cert bytes];
|
||||
const char* buffer = [[CryptoManager nullTerminateString:cert] bytes];
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, buffer);
|
||||
@@ -86,7 +91,7 @@ static NSData* p12 = nil;
|
||||
BIO_free(bio);
|
||||
|
||||
if (!x509) {
|
||||
NSLog(@"ERROR: unable to parse certificate in memory");
|
||||
Log(LOG_E, @"Unable to parse certificate in memory");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -105,7 +110,7 @@ static NSData* p12 = nil;
|
||||
}
|
||||
|
||||
- (NSData *)signData:(NSData *)data withKey:(NSData *)key {
|
||||
const char* buffer = [key bytes];
|
||||
const char* buffer = [[CryptoManager nullTerminateString:key] bytes];
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, buffer);
|
||||
|
||||
@@ -115,7 +120,7 @@ static NSData* p12 = nil;
|
||||
BIO_free(bio);
|
||||
|
||||
if (!pkey) {
|
||||
NSLog(@"ERROR: unable to parse private key in memory!");
|
||||
Log(LOG_E, @"Unable to parse private key in memory!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -188,14 +193,14 @@ static NSData* p12 = nil;
|
||||
}
|
||||
|
||||
+ (NSData *)getSignatureFromCert:(NSData *)cert {
|
||||
const char* buffer = [cert bytes];
|
||||
const char* buffer = [[CryptoManager nullTerminateString:cert] bytes];
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, buffer);
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
if (!x509) {
|
||||
NSLog(@"ERROR: unable to parse certificate in memory!");
|
||||
Log(LOG_E, @"Unable to parse certificate in memory!");
|
||||
return NULL;
|
||||
}
|
||||
return [NSData dataWithBytes:x509->signature->data length:x509->signature->length];
|
||||
@@ -206,7 +211,7 @@ static NSData* p12 = nil;
|
||||
dispatch_once(&pred, ^{
|
||||
if (![CryptoManager keyPairExists]) {
|
||||
|
||||
NSLog(@"Generating Certificate... ");
|
||||
Log(LOG_I, @"Generating Certificate... ");
|
||||
CertKeyPair certKeyPair = generateCertKeyPair();
|
||||
|
||||
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
@@ -215,26 +220,12 @@ static NSData* p12 = nil;
|
||||
NSString* keyPairFile = [documentsDirectory stringByAppendingPathComponent:@"client.key"];
|
||||
NSString* p12File = [documentsDirectory stringByAppendingPathComponent:@"client.p12"];
|
||||
|
||||
//NSLog(@"Writing cert and key to: \n%@\n%@", certFile, keyPairFile);
|
||||
//Log(LOG_D, @"Writing cert and key to: \n%@\n%@", certFile, keyPairFile);
|
||||
saveCertKeyPair([certFile UTF8String], [p12File UTF8String], [keyPairFile UTF8String], certKeyPair);
|
||||
freeCertKeyPair(certKeyPair);
|
||||
NSLog(@"Certificate created");
|
||||
Log(LOG_I, @"Certificate created");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (NSString*) getUniqueID {
|
||||
// generate a UUID
|
||||
NSUUID* uuid = [ASIdentifierManager sharedManager].advertisingIdentifier;
|
||||
NSString* idString = [NSString stringWithString:[uuid UUIDString]];
|
||||
|
||||
// we need a 16byte hex-string so we take the last 17 characters
|
||||
// and remove the '-' to get a 16 character string
|
||||
NSMutableString* uniqueId = [NSMutableString stringWithString:[idString substringFromIndex:19]];
|
||||
[uniqueId deleteCharactersInRange:NSMakeRange(4, 1)];
|
||||
|
||||
//NSLog(@"Unique ID: %@", uniqueId);
|
||||
return [NSString stringWithString:uniqueId];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// IdManager.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/31/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface IdManager : NSObject
|
||||
|
||||
+ (NSString*) getUniqueId;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// IdManager.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/31/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "IdManager.h"
|
||||
#import "DataManager.h"
|
||||
|
||||
@implementation IdManager
|
||||
|
||||
+ (NSString*) getUniqueId {
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
|
||||
NSString* uniqueId = [dataMan getUniqueId];
|
||||
if (uniqueId == nil) {
|
||||
uniqueId = [IdManager generateUniqueId];
|
||||
[dataMan updateUniqueId:uniqueId];
|
||||
Log(LOG_I, @"No UUID found. Generated new UUID: %@", uniqueId);
|
||||
}
|
||||
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
+ (NSString*) generateUniqueId {
|
||||
UInt64 uuidLong = ((UInt64) arc4random() << 32) | arc4random();
|
||||
return [NSString stringWithFormat:@"%016llx", uuidLong];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -85,7 +85,6 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
||||
|
||||
if (*pkeyp == NULL) {
|
||||
if ((pk=EVP_PKEY_new()) == NULL) {
|
||||
abort();
|
||||
return(0);
|
||||
}
|
||||
} else {
|
||||
@@ -102,7 +101,6 @@ int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
||||
|
||||
rsa = RSA_generate_key(bits, RSA_F4, NULL, NULL);
|
||||
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// mkcert.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/16/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Limelight_mkcert_h
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// App+CoreDataProperties.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 7/10/15.
|
||||
// Copyright © 2015 Limelight Stream. All rights reserved.
|
||||
//
|
||||
// Delete this file and regenerate it using "Create NSManagedObject Subclass…"
|
||||
// to keep your implementation up to date with your model.
|
||||
//
|
||||
|
||||
#import "App.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface App (CoreDataProperties)
|
||||
|
||||
@property (nullable, nonatomic, retain) NSString *id;
|
||||
@property (nullable, nonatomic, retain) NSData *image;
|
||||
@property (nullable, nonatomic, retain) NSString *name;
|
||||
@property (nullable, nonatomic, retain) Host *host;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// App.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 7/10/15.
|
||||
// Copyright © 2015 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@class Host;
|
||||
|
||||
@interface App : NSManagedObject
|
||||
|
||||
@end
|
||||
|
||||
#import "App+CoreDataProperties.h"
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// App.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 7/10/15.
|
||||
// Copyright © 2015 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "App.h"
|
||||
|
||||
@implementation App
|
||||
|
||||
@end
|
||||
@@ -1,25 +1,31 @@
|
||||
//
|
||||
// DataManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Settings.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "Host.h"
|
||||
#import "TemporaryHost.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "TemporarySettings.h"
|
||||
|
||||
@interface DataManager : NSObject
|
||||
|
||||
@property (strong, nonatomic) AppDelegate* appDelegate;
|
||||
|
||||
- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width onscreenControls:(NSInteger)onscreenControls;
|
||||
- (Settings*) retrieveSettings;
|
||||
- (NSArray*) retrieveHosts;
|
||||
- (void) saveHosts;
|
||||
- (Host*) createHost;
|
||||
- (void) removeHost:(Host*)host;
|
||||
|
||||
- (NSArray*) getHosts;
|
||||
- (void) updateHost:(TemporaryHost*)host;
|
||||
- (void) updateAppsForExistingHost:(TemporaryHost *)host;
|
||||
- (void) updateIconForExistingApp:(TemporaryApp*)app;
|
||||
- (void) removeHost:(TemporaryHost*)host;
|
||||
- (void) removeApp:(TemporaryApp*)app;
|
||||
|
||||
- (TemporarySettings*) getSettings;
|
||||
|
||||
- (void) updateUniqueId:(NSString*)uniqueId;
|
||||
- (NSString*) getUniqueId;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,43 +1,138 @@
|
||||
//
|
||||
// DataManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DataManager.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "TemporarySettings.h"
|
||||
#import "Settings.h"
|
||||
|
||||
@implementation DataManager
|
||||
@implementation DataManager {
|
||||
NSManagedObjectContext *_managedObjectContext;
|
||||
AppDelegate *_appDelegate;
|
||||
}
|
||||
|
||||
- (id) init {
|
||||
self = [super init];
|
||||
self.appDelegate = [[UIApplication sharedApplication] delegate];
|
||||
|
||||
_appDelegate = [[UIApplication sharedApplication] delegate];
|
||||
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
|
||||
[_managedObjectContext setPersistentStoreCoordinator:_appDelegate.persistentStoreCoordinator];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width onscreenControls:(NSInteger)onscreenControls {
|
||||
Settings* settingsToSave = [self retrieveSettings];
|
||||
settingsToSave.framerate = [NSNumber numberWithInteger:framerate];
|
||||
// Bitrate is persisted in kbps
|
||||
settingsToSave.bitrate = [NSNumber numberWithInteger:bitrate * 1000];
|
||||
settingsToSave.height = [NSNumber numberWithInteger:height];
|
||||
settingsToSave.width = [NSNumber numberWithInteger:width];
|
||||
settingsToSave.onscreenControls = [NSNumber numberWithInteger:onscreenControls];
|
||||
- (void) updateUniqueId:(NSString*)uniqueId {
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
[self retrieveSettings].uniqueId = uniqueId;
|
||||
[self saveData];
|
||||
}];
|
||||
}
|
||||
|
||||
NSError* error;
|
||||
if (![[self.appDelegate managedObjectContext] save:&error]) {
|
||||
NSLog(@"ERROR: Unable to save settings to database");
|
||||
}
|
||||
[self.appDelegate saveContext];
|
||||
- (NSString*) getUniqueId {
|
||||
__block NSString *uid;
|
||||
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
uid = [self retrieveSettings].uniqueId;
|
||||
}];
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
- (void) saveSettingsWithBitrate:(NSInteger)bitrate framerate:(NSInteger)framerate height:(NSInteger)height width:(NSInteger)width onscreenControls:(NSInteger)onscreenControls {
|
||||
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
Settings* settingsToSave = [self retrieveSettings];
|
||||
settingsToSave.framerate = [NSNumber numberWithInteger:framerate];
|
||||
// Bitrate is persisted in kbps
|
||||
settingsToSave.bitrate = [NSNumber numberWithInteger:bitrate];
|
||||
settingsToSave.height = [NSNumber numberWithInteger:height];
|
||||
settingsToSave.width = [NSNumber numberWithInteger:width];
|
||||
settingsToSave.onscreenControls = [NSNumber numberWithInteger:onscreenControls];
|
||||
|
||||
[self saveData];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) updateHost:(TemporaryHost *)host {
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
// Add a new persistent managed object if one doesn't exist
|
||||
Host* parent = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
|
||||
if (parent == nil) {
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Host" inManagedObjectContext:_managedObjectContext];
|
||||
parent = [[Host alloc] initWithEntity:entity insertIntoManagedObjectContext:_managedObjectContext];
|
||||
}
|
||||
|
||||
// Push changes from the temp host to the persistent one
|
||||
[host propagateChangesToParent:parent];
|
||||
|
||||
[self saveData];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) updateAppsForExistingHost:(TemporaryHost *)host {
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
Host* parent = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
|
||||
if (parent == nil) {
|
||||
// The host must exist to be updated
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableSet *applist = [[NSMutableSet alloc] init];
|
||||
NSArray *appRecords = [self fetchRecords:@"App"];
|
||||
for (TemporaryApp* app in host.appList) {
|
||||
// Add a new persistent managed object if one doesn't exist
|
||||
App* parentApp = [self getAppForTemporaryApp:app withAppRecords:appRecords];
|
||||
if (parentApp == nil) {
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:_managedObjectContext];
|
||||
parentApp = [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:_managedObjectContext];
|
||||
}
|
||||
|
||||
[app propagateChangesToParent:parentApp withHost:parent];
|
||||
|
||||
[applist addObject:parentApp];
|
||||
}
|
||||
|
||||
parent.appList = applist;
|
||||
|
||||
[self saveData];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) updateIconForExistingApp:(TemporaryApp*)app {
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
App* parentApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]];
|
||||
if (parentApp == nil) {
|
||||
// The app must exist to be updated
|
||||
return;
|
||||
}
|
||||
|
||||
parentApp.image = app.image;
|
||||
|
||||
[self saveData];
|
||||
}];
|
||||
}
|
||||
|
||||
- (TemporarySettings*) getSettings {
|
||||
__block TemporarySettings *tempSettings;
|
||||
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
tempSettings = [[TemporarySettings alloc] initFromSettings:[self retrieveSettings]];
|
||||
}];
|
||||
|
||||
return tempSettings;
|
||||
}
|
||||
|
||||
- (Settings*) retrieveSettings {
|
||||
NSArray* fetchedRecords = [self fetchRecords:@"Settings"];
|
||||
if (fetchedRecords.count == 0) {
|
||||
// create a new settings object with the default values
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Settings" inManagedObjectContext:[self.appDelegate managedObjectContext]];
|
||||
Settings* settings = [[Settings alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]];
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Settings" inManagedObjectContext:_managedObjectContext];
|
||||
Settings* settings = [[Settings alloc] initWithEntity:entity insertIntoManagedObjectContext:_managedObjectContext];
|
||||
|
||||
return settings;
|
||||
} else {
|
||||
@@ -46,40 +141,84 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (Host*) createHost {
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:@"Host" inManagedObjectContext:[self.appDelegate managedObjectContext]];
|
||||
Host* host = [[Host alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]];
|
||||
return host;
|
||||
- (void) removeApp:(TemporaryApp*)app {
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
App* managedApp = [self getAppForTemporaryApp:app withAppRecords:[self fetchRecords:@"App"]];
|
||||
if (managedApp != nil) {
|
||||
[_managedObjectContext deleteObject:managedApp];
|
||||
[self saveData];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) removeHost:(Host*)host {
|
||||
[[self.appDelegate managedObjectContext] deleteObject:host];
|
||||
- (void) removeHost:(TemporaryHost*)host {
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
Host* managedHost = [self getHostForTemporaryHost:host withHostRecords:[self fetchRecords:@"Host"]];
|
||||
if (managedHost != nil) {
|
||||
[_managedObjectContext deleteObject:managedHost];
|
||||
[self saveData];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) saveHosts {
|
||||
- (void) saveData {
|
||||
NSError* error;
|
||||
if (![[self.appDelegate managedObjectContext] save:&error]) {
|
||||
NSLog(@"ERROR: Unable to save hosts to database");
|
||||
if (![_managedObjectContext save:&error]) {
|
||||
Log(LOG_E, @"Unable to save hosts to database: %@", error);
|
||||
}
|
||||
[self.appDelegate saveContext];
|
||||
|
||||
[_appDelegate saveContext];
|
||||
}
|
||||
|
||||
- (NSArray*) retrieveHosts {
|
||||
return [self fetchRecords:@"Host"];
|
||||
- (NSArray*) getHosts {
|
||||
__block NSMutableArray *tempHosts = [[NSMutableArray alloc] init];
|
||||
|
||||
[_managedObjectContext performBlockAndWait:^{
|
||||
NSArray *hosts = [self fetchRecords:@"Host"];
|
||||
|
||||
for (Host* host in hosts) {
|
||||
[tempHosts addObject:[[TemporaryHost alloc] initFromHost:host]];
|
||||
}
|
||||
}];
|
||||
|
||||
return tempHosts;
|
||||
}
|
||||
|
||||
// Only call from within performBlockAndWait!!!
|
||||
- (Host*) getHostForTemporaryHost:(TemporaryHost*)tempHost withHostRecords:(NSArray*)hosts {
|
||||
for (Host* host in hosts) {
|
||||
if ([tempHost.uuid isEqualToString:host.uuid]) {
|
||||
return host;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Only call from within performBlockAndWait!!!
|
||||
- (App*) getAppForTemporaryApp:(TemporaryApp*)tempApp withAppRecords:(NSArray*)apps {
|
||||
for (App* app in apps) {
|
||||
if ([app.id isEqualToString:tempApp.id] &&
|
||||
[app.host.uuid isEqualToString:app.host.uuid]) {
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray*) fetchRecords:(NSString*)entityName {
|
||||
NSArray* fetchedRecords;
|
||||
|
||||
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self.appDelegate managedObjectContext]];
|
||||
NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:_managedObjectContext];
|
||||
[fetchRequest setEntity:entity];
|
||||
[fetchRequest setAffectedStores:[NSArray arrayWithObjects:[[self.appDelegate persistentStoreCoordinator] persistentStoreForURL:[self.appDelegate getStoreURL]], nil]];
|
||||
|
||||
NSError* error;
|
||||
NSArray* fetchedRecords = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error];
|
||||
fetchedRecords = [_managedObjectContext executeFetchRequest:fetchRequest error:&error];
|
||||
//TODO: handle errors
|
||||
|
||||
return fetchedRecords;
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// Host+CoreDataProperties.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 7/10/15.
|
||||
// Copyright © 2015 Limelight Stream. All rights reserved.
|
||||
//
|
||||
// Delete this file and regenerate it using "Create NSManagedObject Subclass…"
|
||||
// to keep your implementation up to date with your model.
|
||||
//
|
||||
|
||||
#import "Host.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface Host (CoreDataProperties)
|
||||
|
||||
@property (nullable, nonatomic, retain) NSString *address;
|
||||
@property (nullable, nonatomic, retain) NSString *externalAddress;
|
||||
@property (nullable, nonatomic, retain) NSString *localAddress;
|
||||
@property (nullable, nonatomic, retain) NSString *mac;
|
||||
@property (nullable, nonatomic, retain) NSString *name;
|
||||
@property (nullable, nonatomic, retain) NSNumber *pairState;
|
||||
@property (nullable, nonatomic, retain) NSString *uuid;
|
||||
@property (nullable, nonatomic, retain) NSSet *appList;
|
||||
|
||||
@end
|
||||
|
||||
@interface Host (CoreDataGeneratedAccessors)
|
||||
|
||||
- (void)addAppListObject:(NSManagedObject *)value;
|
||||
- (void)removeAppListObject:(NSManagedObject *)value;
|
||||
- (void)addAppList:(NSSet *)values;
|
||||
- (void)removeAppList:(NSSet *)values;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Host.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Created by Diego Waxemberg on 7/10/15.
|
||||
// Copyright © 2015 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -12,13 +12,6 @@
|
||||
|
||||
@interface Host : NSManagedObject
|
||||
|
||||
@property (nonatomic, retain) NSString * name;
|
||||
@property (nonatomic, retain) NSString * address;
|
||||
@property (nonatomic, retain) NSString * localAddress;
|
||||
@property (nonatomic, retain) NSString * externalAddress;
|
||||
@property (nonatomic, retain) NSString * uuid;
|
||||
@property (nonatomic, retain) NSString * mac;
|
||||
@property (nonatomic) BOOL online;
|
||||
@property (nonatomic) PairState pairState;
|
||||
|
||||
@end
|
||||
|
||||
#import "Host+CoreDataProperties.h"
|
||||
|
||||
@@ -1,23 +1,13 @@
|
||||
//
|
||||
// Host.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Created by Diego Waxemberg on 7/10/15.
|
||||
// Copyright © 2015 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Host.h"
|
||||
|
||||
|
||||
@implementation Host
|
||||
|
||||
@dynamic name;
|
||||
@dynamic address;
|
||||
@dynamic localAddress;
|
||||
@dynamic externalAddress;
|
||||
@dynamic uuid;
|
||||
@dynamic mac;
|
||||
@dynamic pairState;
|
||||
@synthesize online;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Settings.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -17,5 +17,6 @@
|
||||
@property (nonatomic, retain) NSNumber * height;
|
||||
@property (nonatomic, retain) NSNumber * width;
|
||||
@property (nonatomic, retain) NSNumber * onscreenControls;
|
||||
@property (nonatomic, retain) NSString * uniqueId;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Settings.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Settings.h"
|
||||
@@ -16,5 +16,6 @@
|
||||
@dynamic height;
|
||||
@dynamic width;
|
||||
@dynamic onscreenControls;
|
||||
@dynamic uniqueId;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// TemporaryApp.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TemporaryHost.h"
|
||||
#import "App.h"
|
||||
|
||||
@interface TemporaryApp : NSObject
|
||||
|
||||
@property (nullable, nonatomic, retain) NSString *id;
|
||||
@property (nullable, nonatomic, retain) NSData *image;
|
||||
@property (nullable, nonatomic, retain) NSString *name;
|
||||
@property (nonatomic) BOOL isRunning;
|
||||
@property (nullable, nonatomic, retain) TemporaryHost *host;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (id) initFromApp:(App*)app withTempHost:(TemporaryHost*)tempHost;
|
||||
|
||||
- (NSComparisonResult)compareName:(TemporaryApp *)other;
|
||||
|
||||
- (void) propagateChangesToParent:(App*)parent withHost:(Host*)host;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// TemporaryApp.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TemporaryApp.h"
|
||||
#import "App.h"
|
||||
|
||||
@implementation TemporaryApp
|
||||
|
||||
- (id) initFromApp:(App*)app withTempHost:(TemporaryHost*)tempHost {
|
||||
self = [self init];
|
||||
|
||||
self.id = app.id;
|
||||
self.image = app.image;
|
||||
self.name = app.name;
|
||||
self.host = tempHost;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) propagateChangesToParent:(App*)parent withHost:(Host*)host {
|
||||
parent.id = self.id;
|
||||
parent.name = self.name;
|
||||
parent.host = host;
|
||||
}
|
||||
|
||||
- (NSComparisonResult)compareName:(TemporaryApp *)other {
|
||||
return [self.name caseInsensitiveCompare:other.name];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return [self.host.uuid hash] * 31 + [self.id intValue];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (![object isKindOfClass:[App class]]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [self.host.uuid isEqualToString:((App*)object).host.uuid] &&
|
||||
[self.id isEqualToString:((App*)object).id];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// TemporaryHost.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 12/1/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Utils.h"
|
||||
#import "Host.h"
|
||||
|
||||
@interface TemporaryHost : NSObject
|
||||
|
||||
@property (nonatomic) BOOL online;
|
||||
@property (nonatomic) PairState pairState;
|
||||
@property (nonatomic, nullable) NSString * activeAddress;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (nonatomic, retain) NSString *address;
|
||||
@property (nonatomic, retain) NSString *externalAddress;
|
||||
@property (nonatomic, retain) NSString *localAddress;
|
||||
@property (nonatomic, retain) NSString *mac;
|
||||
@property (nonatomic, retain) NSString *name;
|
||||
@property (nonatomic, retain) NSString *uuid;
|
||||
@property (nonatomic, retain) NSMutableSet *appList;
|
||||
|
||||
- (id) initFromHost:(Host*)host;
|
||||
|
||||
- (NSComparisonResult)compareName:(TemporaryHost *)other;
|
||||
|
||||
- (void) propagateChangesToParent:(Host*)host;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// TemporaryHost.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 12/1/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DataManager.h"
|
||||
#import "TemporaryHost.h"
|
||||
#import "Host.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "App.h"
|
||||
|
||||
@implementation TemporaryHost
|
||||
|
||||
- (id) init {
|
||||
self = [super init];
|
||||
self.appList = [[NSMutableSet alloc] init];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initFromHost:(Host*)host {
|
||||
self = [self init];
|
||||
|
||||
self.address = host.address;
|
||||
self.externalAddress = host.externalAddress;
|
||||
self.localAddress = host.localAddress;
|
||||
self.mac = host.mac;
|
||||
self.name = host.name;
|
||||
self.uuid = host.uuid;
|
||||
self.pairState = [host.pairState intValue];
|
||||
|
||||
NSMutableSet *appList = [[NSMutableSet alloc] init];
|
||||
|
||||
for (App* app in host.appList) {
|
||||
TemporaryApp *tempApp = [[TemporaryApp alloc] initFromApp:app withTempHost:self];
|
||||
[appList addObject:tempApp];
|
||||
}
|
||||
|
||||
self.appList = appList;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) propagateChangesToParent:(Host*)parentHost {
|
||||
parentHost.address = self.address;
|
||||
parentHost.externalAddress = self.externalAddress;
|
||||
parentHost.localAddress = self.localAddress;
|
||||
parentHost.mac = self.mac;
|
||||
parentHost.name = self.name;
|
||||
parentHost.uuid = self.uuid;
|
||||
parentHost.pairState = [NSNumber numberWithInt:self.pairState];
|
||||
}
|
||||
|
||||
- (NSComparisonResult)compareName:(TemporaryHost *)other {
|
||||
return [self.name caseInsensitiveCompare:other.name];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return [self.uuid hash];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if (self == object) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (![object isKindOfClass:[Host class]]) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return [self.uuid isEqualToString:((Host*)object).uuid];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TemporarySettings.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 12/1/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Settings.h"
|
||||
|
||||
@interface TemporarySettings : NSObject
|
||||
|
||||
@property (nonatomic, retain) Settings * parent;
|
||||
|
||||
@property (nonatomic, retain) NSNumber * bitrate;
|
||||
@property (nonatomic, retain) NSNumber * framerate;
|
||||
@property (nonatomic, retain) NSNumber * height;
|
||||
@property (nonatomic, retain) NSNumber * width;
|
||||
@property (nonatomic, retain) NSNumber * onscreenControls;
|
||||
@property (nonatomic, retain) NSString * uniqueId;
|
||||
|
||||
- (id) initFromSettings:(Settings*)settings;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// TemporarySettings.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 12/1/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TemporarySettings.h"
|
||||
|
||||
@implementation TemporarySettings
|
||||
|
||||
- (id) initFromSettings:(Settings*)settings {
|
||||
self = [self init];
|
||||
|
||||
self.parent = settings;
|
||||
|
||||
self.bitrate = settings.bitrate;
|
||||
self.framerate = settings.framerate;
|
||||
self.height = settings.height;
|
||||
self.width = settings.width;
|
||||
self.onscreenControls = settings.onscreenControls;
|
||||
self.uniqueId = settings.uniqueId;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "a_button.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "a_button-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "a_button-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -1,80 +1,86 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x",
|
||||
"filename" : "Icon-Small@2x.png"
|
||||
"idiom" : "iphone",
|
||||
"filename" : "app_icon_settings_58x58_@2x-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x",
|
||||
"filename" : "Icon-Small@3x.png"
|
||||
"idiom" : "iphone",
|
||||
"filename" : "app_icon_settings_87x87_@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x",
|
||||
"filename" : "Icon-40@2x.png"
|
||||
"idiom" : "iphone",
|
||||
"filename" : "app_icon_spotlight_80x80_@2x-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x",
|
||||
"filename" : "Icon-40@3x.png"
|
||||
"idiom" : "iphone",
|
||||
"filename" : "app_icon_spotlight_120x120_@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x",
|
||||
"filename" : "[email protected]"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"scale" : "3x",
|
||||
"filename" : "Icon-60@3x.png"
|
||||
"idiom" : "iphone",
|
||||
"filename" : "app_icon_180x180_@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x",
|
||||
"filename" : "Icon-Small.png"
|
||||
"idiom" : "ipad",
|
||||
"filename" : "app_icon_settings_29x29_@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x",
|
||||
"filename" : "Icon-Small@2x.png"
|
||||
"idiom" : "ipad",
|
||||
"filename" : "app_icon_settings_58x58_@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x",
|
||||
"filename" : "Icon-40.png"
|
||||
"idiom" : "ipad",
|
||||
"filename" : "app_icon_spotlight_40x40_@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x",
|
||||
"filename" : "Icon-40@2x.png"
|
||||
"idiom" : "ipad",
|
||||
"filename" : "app_icon_spotlight_80x80_@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x",
|
||||
"filename" : "Icon-76.png"
|
||||
"idiom" : "ipad",
|
||||
"filename" : "app_icon_76x76_@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x",
|
||||
"filename" : "Icon-76@2x.png"
|
||||
"idiom" : "ipad",
|
||||
"filename" : "app_icon_152x152_@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "makeappicon"
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 34 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 41 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "b_button.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "b_button-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "b_button-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "down.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "down-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "down-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 455 B |
|
Before Width: | Height: | Size: 455 B |
|
Before Width: | Height: | Size: 455 B After Width: | Height: | Size: 455 B |
|
After Width: | Height: | Size: 3.6 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "L1-1.png"
|
||||
"filename" : "L1_40x40@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "L1.png"
|
||||
"filename" : "L1_80x80@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "L1-2.png"
|
||||
"filename" : "L1_120x120@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 6.3 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "L2.png"
|
||||
"filename" : "L2_60x60@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "L2-1.png"
|
||||
"filename" : "L2_120x120@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "L2-2.png"
|
||||
"filename" : "L2_180x180@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "L3.png"
|
||||
"filename" : "L3_80x80@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "L3-1.png"
|
||||
"filename" : "L3_160x160@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "L3-2.png"
|
||||
"filename" : "L3_240x240@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"subtype" : "retina4",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "left.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "left-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "left-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 525 B |
|
Before Width: | Height: | Size: 525 B |
|
After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 525 B After Width: | Height: | Size: 525 B |
|
Before Width: | Height: | Size: 138 KiB |
@@ -2,7 +2,16 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Limelight-logo.pdf"
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "logo_universal.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -7,7 +7,7 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "Limelight Flat Design Banner.png"
|
||||
"filename" : "play.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
|
After Width: | Height: | Size: 17 KiB |
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 901 B |
|
After Width: | Height: | Size: 812 B |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "R1.png"
|
||||
"filename" : "R1_40x40@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "R1-1.png"
|
||||
"filename" : "R1_80x80@1x-1.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "R1-2.png"
|
||||
"filename" : "R1_120x120@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "R2-1.png"
|
||||
"filename" : "R2_60x60@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "R2.png"
|
||||
"filename" : "R2_120x120@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "R2-2.png"
|
||||
"filename" : "R2_120x120@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "R3.png"
|
||||
"filename" : "R3_80x80@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "R3-1.png"
|
||||
"filename" : "R3_160x160@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "R3-2.png"
|
||||
"filename" : "R3_240x240@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "right.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "right-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "right-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 549 B |
|
Before Width: | Height: | Size: 549 B |
|
After Width: | Height: | Size: 3.5 KiB |
|
Before Width: | Height: | Size: 549 B After Width: | Height: | Size: 549 B |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "select_button-1.png"
|
||||
"filename" : "select_button_54x24@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "select_button.png"
|
||||
"filename" : "select_button_108x48@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "select_button-2.png"
|
||||
"filename" : "select_button_162x72@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 735 B |
|
Before Width: | Height: | Size: 735 B |
|
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 735 B |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "settings_1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "settings_2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "settings_3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "start_button-1.png"
|
||||
"filename" : "start_button_54x24@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "start_button.png"
|
||||
"filename" : "start_button_108x48@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "start_button-2.png"
|
||||
"filename" : "start_button_162x72@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 774 B |
|
Before Width: | Height: | Size: 774 B |
|
Before Width: | Height: | Size: 774 B After Width: | Height: | Size: 774 B |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "stick_inner-1.png"
|
||||
"filename" : "stick_inner_48x49@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "stick_inner.png"
|
||||
"filename" : "stick_inner_96x97@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "stick_inner-2.png"
|
||||
"filename" : "stick_inner_144x146@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -3,17 +3,17 @@
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "stick_outer-1.png"
|
||||
"filename" : "stick_outer_80x80@1x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "stick_outer.png"
|
||||
"filename" : "stick_outer_160x160@2x.png"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "stick_outer-2.png"
|
||||
"filename" : "stick_outer_240x240@3x.png"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "up-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "up.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "up-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 452 B |
|
Before Width: | Height: | Size: 452 B |
|
Before Width: | Height: | Size: 452 B After Width: | Height: | Size: 452 B |
|
After Width: | Height: | Size: 3.4 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "x_button-1.png"
|
||||
"filename" : "x[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "x_button.png"
|
||||
"filename" : "x[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "x_button-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
@@ -2,18 +2,17 @@
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x",
|
||||
"filename" : "y_button.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
"filename" : "y_button-1.png"
|
||||
"filename" : "[email protected]",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x",
|
||||
"filename" : "y_button-2.png"
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
|
||||
|
Before Width: | Height: | Size: 969 B |
|
Before Width: | Height: | Size: 969 B |
|
After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 969 B After Width: | Height: | Size: 969 B |
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Controller.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface Controller : NSObject
|
||||
|
||||
@property (nonatomic) int playerIndex;
|
||||
@property (nonatomic) int lastButtonFlags;
|
||||
@property (nonatomic) int emulatingButtonFlags;
|
||||
@property (nonatomic) char lastLeftTrigger;
|
||||
@property (nonatomic) char lastRightTrigger;
|
||||
@property (nonatomic) short lastLeftStickX;
|
||||
@property (nonatomic) short lastLeftStickY;
|
||||
@property (nonatomic) short lastRightStickX;
|
||||
@property (nonatomic) short lastRightStickY;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// Controller.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Controller.h"
|
||||
|
||||
@implementation Controller
|
||||
@synthesize playerIndex;
|
||||
@synthesize lastButtonFlags, emulatingButtonFlags, lastLeftTrigger, lastRightTrigger;
|
||||
@synthesize lastLeftStickX, lastLeftStickY, lastRightStickX, lastRightStickY;
|
||||
@end
|
||||
@@ -1,12 +1,13 @@
|
||||
//
|
||||
// ControllerSupport.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Controller.h"
|
||||
|
||||
@class OnScreenControls;
|
||||
|
||||
@@ -16,18 +17,18 @@
|
||||
-(void) initAutoOnScreenControlMode:(OnScreenControls*)osc;
|
||||
-(void) cleanup;
|
||||
|
||||
-(void) updateLeftStick:(short)x y:(short)y;
|
||||
-(void) updateRightStick:(short)x y:(short)y;
|
||||
-(void) updateLeftStick:(Controller*)controller x:(short)x y:(short)y;
|
||||
-(void) updateRightStick:(Controller*)controller x:(short)x y:(short)y;
|
||||
|
||||
-(void) updateLeftTrigger:(char)left;
|
||||
-(void) updateRightTrigger:(char)right;
|
||||
-(void) updateTriggers:(char)left right:(char)right;
|
||||
-(void) updateLeftTrigger:(Controller*)controller left:(char)left;
|
||||
-(void) updateRightTrigger:(Controller*)controller right:(char)right;
|
||||
-(void) updateTriggers:(Controller*)controller left:(char)left right:(char)right;
|
||||
|
||||
-(void) updateButtonFlags:(int)flags;
|
||||
-(void) setButtonFlag:(int)flags;
|
||||
-(void) clearButtonFlag:(int)flags;
|
||||
-(void) updateButtonFlags:(Controller*)controller flags:(int)flags;
|
||||
-(void) setButtonFlag:(Controller*)controller flags:(int)flags;
|
||||
-(void) clearButtonFlag:(Controller*)controller flags:(int)flags;
|
||||
|
||||
-(void) updateFinished;
|
||||
-(void) updateFinished:(Controller*)controller;
|
||||
|
||||
@property (nonatomic, strong) id connectObserver;
|
||||
@property (nonatomic, strong) id disconnectObserver;
|
||||
|
||||
@@ -1,245 +1,235 @@
|
||||
//
|
||||
// ControllerSupport.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ControllerSupport.h"
|
||||
#import "OnScreenControls.h"
|
||||
#import "Controller.h"
|
||||
#include "Limelight.h"
|
||||
|
||||
@import GameController;
|
||||
|
||||
@implementation ControllerSupport {
|
||||
NSLock *_controllerValueLock;
|
||||
NSLock *_controllerStreamLock;
|
||||
NSMutableDictionary *_controllers;
|
||||
|
||||
OnScreenControls *_osc;
|
||||
|
||||
int _lastButtonFlags;
|
||||
char _lastLeftTrigger, _lastRightTrigger;
|
||||
short _lastLeftStickX, _lastLeftStickY;
|
||||
short _lastRightStickX, _lastRightStickY;
|
||||
char _controllerNumbers;
|
||||
|
||||
#define EMULATING_SELECT 0x1
|
||||
#define EMULATING_SPECIAL 0x2
|
||||
int _emulatingButtonFlags;
|
||||
}
|
||||
|
||||
// UPDATE_BUTTON_FLAG(flag, pressed)
|
||||
#define UPDATE_BUTTON_FLAG(x, y) \
|
||||
((y) ? [self setButtonFlag:x] : [self clearButtonFlag:x])
|
||||
// UPDATE_BUTTON_FLAG(controller, flag, pressed)
|
||||
#define UPDATE_BUTTON_FLAG(controller, x, y) \
|
||||
((y) ? [self setButtonFlag:controller flags:x] : [self clearButtonFlag:controller flags:x])
|
||||
|
||||
-(void) updateLeftStick:(short)x y:(short)y
|
||||
-(void) updateLeftStick:(Controller*)controller x:(short)x y:(short)y
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastLeftStickX = x;
|
||||
_lastLeftStickY = y;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateRightStick:(short)x y:(short)y
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastRightStickX = x;
|
||||
_lastRightStickY = y;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateLeftTrigger:(char)left
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastLeftTrigger = left;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateRightTrigger:(char)right
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastRightTrigger = right;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateTriggers:(char)left right:(char)right
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastLeftTrigger = left;
|
||||
_lastRightTrigger = right;
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) handleSpecialCombosReleased:(int)releasedButtons
|
||||
{
|
||||
if ((_emulatingButtonFlags & EMULATING_SELECT) &&
|
||||
((releasedButtons & LB_FLAG) || (releasedButtons & PLAY_FLAG))) {
|
||||
_lastButtonFlags &= ~BACK_FLAG;
|
||||
_emulatingButtonFlags &= ~EMULATING_SELECT;
|
||||
@synchronized(controller) {
|
||||
controller.lastLeftStickX = x;
|
||||
controller.lastLeftStickY = y;
|
||||
}
|
||||
if ((_emulatingButtonFlags & EMULATING_SPECIAL) &&
|
||||
}
|
||||
|
||||
-(void) updateRightStick:(Controller*)controller x:(short)x y:(short)y
|
||||
{
|
||||
@synchronized(controller) {
|
||||
controller.lastRightStickX = x;
|
||||
controller.lastRightStickY = y;
|
||||
}
|
||||
}
|
||||
|
||||
-(void) updateLeftTrigger:(Controller*)controller left:(char)left
|
||||
{
|
||||
@synchronized(controller) {
|
||||
controller.lastLeftTrigger = left;
|
||||
}
|
||||
}
|
||||
|
||||
-(void) updateRightTrigger:(Controller*)controller right:(char)right
|
||||
{
|
||||
@synchronized(controller) {
|
||||
controller.lastRightTrigger = right;
|
||||
}
|
||||
}
|
||||
|
||||
-(void) updateTriggers:(Controller*) controller left:(char)left right:(char)right
|
||||
{
|
||||
@synchronized(controller) {
|
||||
controller.lastLeftTrigger = left;
|
||||
controller.lastRightTrigger = right;
|
||||
}
|
||||
}
|
||||
|
||||
-(void) handleSpecialCombosReleased:(Controller*)controller releasedButtons:(int)releasedButtons
|
||||
{
|
||||
if ((controller.emulatingButtonFlags & EMULATING_SELECT) &&
|
||||
((releasedButtons & LB_FLAG) || (releasedButtons & PLAY_FLAG))) {
|
||||
controller.lastButtonFlags &= ~BACK_FLAG;
|
||||
controller.emulatingButtonFlags &= ~EMULATING_SELECT;
|
||||
}
|
||||
if ((controller.emulatingButtonFlags & EMULATING_SPECIAL) &&
|
||||
((releasedButtons & RB_FLAG) || (releasedButtons & PLAY_FLAG) ||
|
||||
(releasedButtons & BACK_FLAG))) {
|
||||
_lastButtonFlags &= ~SPECIAL_FLAG;
|
||||
_emulatingButtonFlags &= ~EMULATING_SPECIAL;
|
||||
}
|
||||
controller.lastButtonFlags &= ~SPECIAL_FLAG;
|
||||
controller.emulatingButtonFlags &= ~EMULATING_SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
-(void) handleSpecialCombosPressed:(int)pressedButtons
|
||||
-(void) handleSpecialCombosPressed:(Controller*)controller pressedButtons:(int)pressedButtons
|
||||
{
|
||||
// Special button combos for select and special
|
||||
if (_lastButtonFlags & PLAY_FLAG) {
|
||||
if (controller.lastButtonFlags & PLAY_FLAG) {
|
||||
// If LB and start are down, trigger select
|
||||
if (_lastButtonFlags & LB_FLAG) {
|
||||
_lastButtonFlags |= BACK_FLAG;
|
||||
_lastButtonFlags &= ~(pressedButtons & (PLAY_FLAG | LB_FLAG));
|
||||
_emulatingButtonFlags |= EMULATING_SELECT;
|
||||
if (controller.lastButtonFlags & LB_FLAG) {
|
||||
controller.lastButtonFlags |= BACK_FLAG;
|
||||
controller.lastButtonFlags &= ~(pressedButtons & (PLAY_FLAG | LB_FLAG));
|
||||
controller.emulatingButtonFlags |= EMULATING_SELECT;
|
||||
}
|
||||
// If (RB or select) and start are down, trigger special
|
||||
else if ((_lastButtonFlags & RB_FLAG) || (_lastButtonFlags & BACK_FLAG)) {
|
||||
_lastButtonFlags |= SPECIAL_FLAG;
|
||||
_lastButtonFlags &= ~(pressedButtons & (PLAY_FLAG | RB_FLAG | BACK_FLAG));
|
||||
_emulatingButtonFlags |= EMULATING_SPECIAL;
|
||||
else if ((controller.lastButtonFlags & RB_FLAG) || (controller.lastButtonFlags & BACK_FLAG)) {
|
||||
controller.lastButtonFlags |= SPECIAL_FLAG;
|
||||
controller.lastButtonFlags &= ~(pressedButtons & (PLAY_FLAG | RB_FLAG | BACK_FLAG));
|
||||
controller.emulatingButtonFlags |= EMULATING_SPECIAL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-(void) updateButtonFlags:(Controller*)controller flags:(int)flags
|
||||
{
|
||||
@synchronized(controller) {
|
||||
int releasedButtons = (controller.lastButtonFlags ^ flags) & ~flags;
|
||||
int pressedButtons = (controller.lastButtonFlags ^ flags) & flags;
|
||||
|
||||
controller.lastButtonFlags = flags;
|
||||
|
||||
// This must be called before handleSpecialCombosPressed
|
||||
// because we clear the original button flags there
|
||||
[self handleSpecialCombosReleased:controller releasedButtons:releasedButtons];
|
||||
|
||||
[self handleSpecialCombosPressed:controller pressedButtons:pressedButtons];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
-(void) updateButtonFlags:(int)flags
|
||||
-(void) setButtonFlag:(Controller*)controller flags:(int)flags
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
int releasedButtons = (_lastButtonFlags ^ flags) & ~flags;
|
||||
int pressedButtons = (_lastButtonFlags ^ flags) & flags;
|
||||
|
||||
_lastButtonFlags = flags;
|
||||
|
||||
// This must be called before handleSpecialCombosPressed
|
||||
// because we clear the original button flags there
|
||||
[self handleSpecialCombosReleased: releasedButtons];
|
||||
|
||||
[self handleSpecialCombosPressed: pressedButtons];
|
||||
|
||||
[_controllerValueLock unlock];
|
||||
@synchronized(controller) {
|
||||
controller.lastButtonFlags |= flags;
|
||||
[self handleSpecialCombosPressed:controller pressedButtons:flags];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) setButtonFlag:(int)flags
|
||||
-(void) clearButtonFlag:(Controller*)controller flags:(int)flags
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastButtonFlags |= flags;
|
||||
[self handleSpecialCombosPressed: flags];
|
||||
[_controllerValueLock unlock];
|
||||
@synchronized(controller) {
|
||||
controller.lastButtonFlags &= ~flags;
|
||||
[self handleSpecialCombosReleased:controller releasedButtons:flags];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) clearButtonFlag:(int)flags
|
||||
{
|
||||
[_controllerValueLock lock];
|
||||
_lastButtonFlags &= ~flags;
|
||||
[self handleSpecialCombosReleased: flags];
|
||||
[_controllerValueLock unlock];
|
||||
}
|
||||
|
||||
-(void) updateFinished
|
||||
-(void) updateFinished:(Controller*)controller
|
||||
{
|
||||
[_controllerStreamLock lock];
|
||||
[_controllerValueLock lock];
|
||||
|
||||
LiSendControllerEvent(_lastButtonFlags, _lastLeftTrigger, _lastRightTrigger, _lastLeftStickX, _lastLeftStickY, _lastRightStickX, _lastRightStickY);
|
||||
|
||||
[_controllerValueLock unlock];
|
||||
@synchronized(controller) {
|
||||
LiSendMultiControllerEvent(controller.playerIndex, controller.lastButtonFlags, controller.lastLeftTrigger, controller.lastRightTrigger, controller.lastLeftStickX, controller.lastLeftStickY, controller.lastRightStickX, controller.lastRightStickY);
|
||||
}
|
||||
[_controllerStreamLock unlock];
|
||||
}
|
||||
|
||||
-(void) unregisterControllerCallbacks
|
||||
-(void) unregisterControllerCallbacks:(GCController*) controller
|
||||
{
|
||||
for (int i = 0; i < [[GCController controllers] count]; i++) {
|
||||
GCController *controller = [GCController controllers][i];
|
||||
if (controller != NULL) {
|
||||
controller.controllerPausedHandler = NULL;
|
||||
|
||||
if (controller != NULL) {
|
||||
controller.controllerPausedHandler = NULL;
|
||||
|
||||
if (controller.extendedGamepad != NULL) {
|
||||
controller.extendedGamepad.valueChangedHandler = NULL;
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
controller.gamepad.valueChangedHandler = NULL;
|
||||
}
|
||||
if (controller.extendedGamepad != NULL) {
|
||||
controller.extendedGamepad.valueChangedHandler = NULL;
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
controller.gamepad.valueChangedHandler = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) registerControllerCallbacks
|
||||
-(void) registerControllerCallbacks:(GCController*) controller
|
||||
{
|
||||
for (int i = 0; i < [[GCController controllers] count]; i++) {
|
||||
GCController *controller = [GCController controllers][i];
|
||||
|
||||
if (controller != NULL) {
|
||||
NSLog(@"Controller connected!");
|
||||
controller.controllerPausedHandler = ^(GCController *controller) {
|
||||
[self setButtonFlag:PLAY_FLAG];
|
||||
[self updateFinished];
|
||||
|
||||
// Pause for 100 ms
|
||||
usleep(100 * 1000);
|
||||
|
||||
[self clearButtonFlag:PLAY_FLAG];
|
||||
[self updateFinished];
|
||||
};
|
||||
if (controller != NULL) {
|
||||
controller.controllerPausedHandler = ^(GCController *controller) {
|
||||
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
|
||||
[self setButtonFlag:limeController flags:PLAY_FLAG];
|
||||
[self updateFinished:limeController];
|
||||
|
||||
if (controller.extendedGamepad != NULL) {
|
||||
controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad *gamepad, GCControllerElement *element) {
|
||||
short leftStickX, leftStickY;
|
||||
short rightStickX, rightStickY;
|
||||
char leftTrigger, rightTrigger;
|
||||
|
||||
UPDATE_BUTTON_FLAG(A_FLAG, gamepad.buttonA.pressed);
|
||||
UPDATE_BUTTON_FLAG(B_FLAG, gamepad.buttonB.pressed);
|
||||
UPDATE_BUTTON_FLAG(X_FLAG, gamepad.buttonX.pressed);
|
||||
UPDATE_BUTTON_FLAG(Y_FLAG, gamepad.buttonY.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(UP_FLAG, gamepad.dpad.up.pressed);
|
||||
UPDATE_BUTTON_FLAG(DOWN_FLAG, gamepad.dpad.down.pressed);
|
||||
UPDATE_BUTTON_FLAG(LEFT_FLAG, gamepad.dpad.left.pressed);
|
||||
UPDATE_BUTTON_FLAG(RIGHT_FLAG, gamepad.dpad.right.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(LB_FLAG, gamepad.leftShoulder.pressed);
|
||||
UPDATE_BUTTON_FLAG(RB_FLAG, gamepad.rightShoulder.pressed);
|
||||
|
||||
leftStickX = gamepad.leftThumbstick.xAxis.value * 0x7FFE;
|
||||
leftStickY = gamepad.leftThumbstick.yAxis.value * 0x7FFE;
|
||||
|
||||
rightStickX = gamepad.rightThumbstick.xAxis.value * 0x7FFE;
|
||||
rightStickY = gamepad.rightThumbstick.yAxis.value * 0x7FFE;
|
||||
|
||||
leftTrigger = gamepad.leftTrigger.value * 0xFF;
|
||||
rightTrigger = gamepad.rightTrigger.value * 0xFF;
|
||||
|
||||
[self updateLeftStick:leftStickX y:leftStickY];
|
||||
[self updateRightStick:rightStickX y:rightStickY];
|
||||
[self updateTriggers:leftTrigger right:rightTrigger];
|
||||
[self updateFinished];
|
||||
};
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad, GCControllerElement *element) {
|
||||
|
||||
UPDATE_BUTTON_FLAG(A_FLAG, gamepad.buttonA.pressed);
|
||||
UPDATE_BUTTON_FLAG(B_FLAG, gamepad.buttonB.pressed);
|
||||
UPDATE_BUTTON_FLAG(X_FLAG, gamepad.buttonX.pressed);
|
||||
UPDATE_BUTTON_FLAG(Y_FLAG, gamepad.buttonY.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(UP_FLAG, gamepad.dpad.up.pressed);
|
||||
UPDATE_BUTTON_FLAG(DOWN_FLAG, gamepad.dpad.down.pressed);
|
||||
UPDATE_BUTTON_FLAG(LEFT_FLAG, gamepad.dpad.left.pressed);
|
||||
UPDATE_BUTTON_FLAG(RIGHT_FLAG, gamepad.dpad.right.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(LB_FLAG, gamepad.leftShoulder.pressed);
|
||||
UPDATE_BUTTON_FLAG(RB_FLAG, gamepad.rightShoulder.pressed);
|
||||
|
||||
[self updateFinished];
|
||||
};
|
||||
}
|
||||
// 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) {
|
||||
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
|
||||
short leftStickX, leftStickY;
|
||||
short rightStickX, rightStickY;
|
||||
char leftTrigger, rightTrigger;
|
||||
|
||||
UPDATE_BUTTON_FLAG(limeController, A_FLAG, gamepad.buttonA.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, B_FLAG, gamepad.buttonB.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, X_FLAG, gamepad.buttonX.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, Y_FLAG, gamepad.buttonY.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(limeController, UP_FLAG, gamepad.dpad.up.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, DOWN_FLAG, gamepad.dpad.down.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, LEFT_FLAG, gamepad.dpad.left.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, RIGHT_FLAG, gamepad.dpad.right.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(limeController, LB_FLAG, gamepad.leftShoulder.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, RB_FLAG, gamepad.rightShoulder.pressed);
|
||||
|
||||
leftStickX = gamepad.leftThumbstick.xAxis.value * 0x7FFE;
|
||||
leftStickY = gamepad.leftThumbstick.yAxis.value * 0x7FFE;
|
||||
|
||||
rightStickX = gamepad.rightThumbstick.xAxis.value * 0x7FFE;
|
||||
rightStickY = gamepad.rightThumbstick.yAxis.value * 0x7FFE;
|
||||
|
||||
leftTrigger = gamepad.leftTrigger.value * 0xFF;
|
||||
rightTrigger = gamepad.rightTrigger.value * 0xFF;
|
||||
|
||||
[self updateLeftStick:limeController x:leftStickX y:leftStickY];
|
||||
[self updateRightStick:limeController x:rightStickX y:rightStickY];
|
||||
[self updateTriggers:limeController left:leftTrigger right:rightTrigger];
|
||||
[self updateFinished:limeController];
|
||||
};
|
||||
}
|
||||
else if (controller.gamepad != NULL) {
|
||||
controller.gamepad.valueChangedHandler = ^(GCGamepad *gamepad, GCControllerElement *element) {
|
||||
Controller* limeController = [_controllers objectForKey:[NSNumber numberWithInteger:gamepad.controller.playerIndex]];
|
||||
UPDATE_BUTTON_FLAG(limeController, A_FLAG, gamepad.buttonA.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, B_FLAG, gamepad.buttonB.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, X_FLAG, gamepad.buttonX.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, Y_FLAG, gamepad.buttonY.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(limeController, UP_FLAG, gamepad.dpad.up.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, DOWN_FLAG, gamepad.dpad.down.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, LEFT_FLAG, gamepad.dpad.left.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, RIGHT_FLAG, gamepad.dpad.right.pressed);
|
||||
|
||||
UPDATE_BUTTON_FLAG(limeController, LB_FLAG, gamepad.leftShoulder.pressed);
|
||||
UPDATE_BUTTON_FLAG(limeController, RB_FLAG, gamepad.rightShoulder.pressed);
|
||||
|
||||
[self updateFinished:limeController];
|
||||
};
|
||||
}
|
||||
} else {
|
||||
Log(LOG_W, @"Tried to register controller callbacks on NULL controller");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-(void) updateAutoOnScreenControlMode
|
||||
@@ -278,37 +268,61 @@
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}
|
||||
|
||||
-(void) assignController:(GCController*)controller {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!(_controllerNumbers & (1 << i))) {
|
||||
_controllerNumbers |= (1 << i);
|
||||
Controller* limeController = [[Controller alloc] init];
|
||||
controller.playerIndex = i;
|
||||
limeController.playerIndex = i;
|
||||
[_controllers setObject:limeController forKey:[NSNumber numberWithInteger:controller.playerIndex]];
|
||||
|
||||
Log(LOG_I, @"Assigning controller index: %d", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(id) init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_controllerStreamLock = [[NSLock alloc] init];
|
||||
_controllerValueLock = [[NSLock alloc] init];
|
||||
_controllers = [[NSMutableDictionary alloc] init];
|
||||
_controllerNumbers = 0;
|
||||
|
||||
Log(LOG_I, @"Number of controllers connected: %ld", (long)[[GCController controllers] count]);
|
||||
for (GCController* controller in [GCController controllers]) {
|
||||
[self assignController:controller];
|
||||
[self registerControllerCallbacks:controller];
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}
|
||||
|
||||
self.connectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
Log(LOG_I, @"Controller connected!");
|
||||
|
||||
GCController* controller = note.object;
|
||||
[self assignController:controller];
|
||||
|
||||
// Register callbacks on the new controller
|
||||
[self registerControllerCallbacks];
|
||||
[self registerControllerCallbacks:controller];
|
||||
|
||||
// Re-evaluate the on-screen control mode
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}];
|
||||
self.disconnectObserver = [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
NSLog(@"Controller disconnected!");
|
||||
Log(LOG_I, @"Controller disconnected!");
|
||||
|
||||
// Reset all controller state to be safe
|
||||
[self updateButtonFlags:0];
|
||||
[self updateLeftStick:0 y:0];
|
||||
[self updateRightStick:0 y:0];
|
||||
[self updateTriggers:0 right:0];
|
||||
[self updateFinished];
|
||||
GCController* controller = note.object;
|
||||
[self unregisterControllerCallbacks:controller];
|
||||
[_controllers removeObjectForKey:[NSNumber numberWithInteger:controller.playerIndex]];
|
||||
_controllerNumbers &= ~(1 << controller.playerIndex);
|
||||
Log(LOG_I, @"Unassigning controller index: %ld", (long)controller.playerIndex);
|
||||
|
||||
// Re-evaluate the on-screen control mode
|
||||
[self updateAutoOnScreenControlMode];
|
||||
}];
|
||||
|
||||
// Register for controller callbacks on any existing controllers
|
||||
[self registerControllerCallbacks];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -316,8 +330,11 @@
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.connectObserver];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self.disconnectObserver];
|
||||
|
||||
[self unregisterControllerCallbacks];
|
||||
[_controllers removeAllObjects];
|
||||
_controllerNumbers = 0;
|
||||
for (GCController* controller in [GCController controllers]) {
|
||||
[self unregisterControllerCallbacks:controller];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
//
|
||||
// OnScreenControls.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 12/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "StreamView.h"
|
||||
|
||||
@class ControllerSupport;
|
||||
|
||||
@@ -23,7 +24,7 @@ typedef NS_ENUM(NSInteger, OnScreenControlsLevel) {
|
||||
OnScreenControlsLevelAutoGCExtendedGamepad,
|
||||
};
|
||||
|
||||
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport;
|
||||
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)edgeDelegate;
|
||||
- (BOOL) handleTouchDownEvent:(NSSet*)touches;
|
||||
- (BOOL) handleTouchUpEvent:(NSSet*)touches;
|
||||
- (BOOL) handleTouchMovedEvent:(NSSet*)touches;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
//
|
||||
// OnScreenControls.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 12/28/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OnScreenControls.h"
|
||||
#import "ControllerSupport.h"
|
||||
#import "Controller.h"
|
||||
#include "Limelight.h"
|
||||
|
||||
#define UPDATE_BUTTON(x, y) (buttonFlags = \
|
||||
@@ -34,6 +35,7 @@
|
||||
CALayer* _l1Button;
|
||||
CALayer* _l2Button;
|
||||
CALayer* _l3Button;
|
||||
CALayer* _edge;
|
||||
|
||||
UITouch* _aTouch;
|
||||
UITouch* _bTouch;
|
||||
@@ -53,6 +55,7 @@
|
||||
UITouch* _l1Touch;
|
||||
UITouch* _l2Touch;
|
||||
UITouch* _l3Touch;
|
||||
UITouch* _edgeTouch;
|
||||
|
||||
NSDate* l3TouchStart;
|
||||
NSDate* r3TouchStart;
|
||||
@@ -60,46 +63,45 @@
|
||||
BOOL l3Set;
|
||||
BOOL r3Set;
|
||||
|
||||
BOOL _iPad;
|
||||
CGRect _controlArea;
|
||||
UIView* _view;
|
||||
OnScreenControlsLevel _level;
|
||||
|
||||
ControllerSupport *_controllerSupport;
|
||||
Controller *_controller;
|
||||
id<EdgeDetectionDelegate> _edgeDelegate;
|
||||
NSMutableArray* _deadTouches;
|
||||
}
|
||||
|
||||
static const float BUTTON_SIZE = 50;
|
||||
static const float EDGE_WIDTH = .05;
|
||||
|
||||
//static const float BUTTON_SIZE = 50;
|
||||
static const float BUTTON_DIST = 20;
|
||||
static float BUTTON_CENTER_X;
|
||||
static float BUTTON_CENTER_Y;
|
||||
|
||||
static const float D_PAD_SHORT = 50;
|
||||
static const float D_PAD_LONG = 65;
|
||||
static const float D_PAD_DIST = 15;
|
||||
static const float D_PAD_DIST = 10;
|
||||
static float D_PAD_CENTER_X;
|
||||
static float D_PAD_CENTER_Y;
|
||||
|
||||
static const float DEAD_ZONE_PADDING = 15;
|
||||
|
||||
static const double STICK_CLICK_RATE = 100;
|
||||
static const float STICK_INNER_SIZE = 80;
|
||||
static const float STICK_OUTER_SIZE = 120;
|
||||
static const float STICK_DEAD_ZONE = .1;
|
||||
static float STICK_INNER_SIZE;
|
||||
static float STICK_OUTER_SIZE;
|
||||
static float LS_CENTER_X;
|
||||
static float LS_CENTER_Y;
|
||||
static float RS_CENTER_X;
|
||||
static float RS_CENTER_Y;
|
||||
|
||||
static const float START_WIDTH = 50;
|
||||
static const float START_HEIGHT = 30;
|
||||
static float START_X;
|
||||
static float START_Y;
|
||||
|
||||
static const float SELECT_WIDTH = 50;
|
||||
static const float SELECT_HEIGHT = 30;
|
||||
static float SELECT_X;
|
||||
static float SELECT_Y;
|
||||
|
||||
static const float BUMPER_SIZE = 45;
|
||||
static const float TRIGGER_SIZE = 60;
|
||||
static const float L3R3_SIZE = 80;
|
||||
|
||||
static float R1_X;
|
||||
static float R1_Y;
|
||||
static float R2_X;
|
||||
@@ -113,10 +115,28 @@ static float L2_Y;
|
||||
static float L3_X;
|
||||
static float L3_Y;
|
||||
|
||||
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport {
|
||||
- (id) initWithView:(UIView*)view controllerSup:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate {
|
||||
self = [self init];
|
||||
_view = view;
|
||||
_controllerSupport = controllerSupport;
|
||||
_controller = [[Controller alloc] init];
|
||||
_controller.playerIndex = 0;
|
||||
_edgeDelegate = swipeDelegate;
|
||||
_deadTouches = [[NSMutableArray alloc] init];
|
||||
|
||||
_iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
|
||||
_controlArea = CGRectMake(0, 0, _view.frame.size.width, _view.frame.size.height);
|
||||
if (_iPad)
|
||||
{
|
||||
// Cut down the control area on an iPad so the controls are more reachable
|
||||
_controlArea.size.height = _view.frame.size.height / 2.0;
|
||||
_controlArea.origin.y = _view.frame.size.height - _controlArea.size.height;
|
||||
}
|
||||
else
|
||||
{
|
||||
_controlArea.origin.x = _controlArea.size.width * EDGE_WIDTH;
|
||||
_controlArea.size.width -= _controlArea.origin.x * 2;
|
||||
}
|
||||
|
||||
_aButton = [CALayer layer];
|
||||
_bButton = [CALayer layer];
|
||||
@@ -138,7 +158,10 @@ static float L3_Y;
|
||||
_rightStickBackground = [CALayer layer];
|
||||
_leftStick = [CALayer layer];
|
||||
_rightStick = [CALayer layer];
|
||||
|
||||
_edge = [CALayer layer];
|
||||
|
||||
[self setupEdgeDetection];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -164,10 +187,10 @@ static float L3_Y;
|
||||
|
||||
[self hideButtons];
|
||||
[self hideBumpers];
|
||||
[self hideL3R3];
|
||||
[self drawTriggers];
|
||||
[self drawStartSelect];
|
||||
[self drawSticks];
|
||||
[self drawL3R3];
|
||||
break;
|
||||
case OnScreenControlsLevelAutoGCExtendedGamepad:
|
||||
// GCExtendedGamepad is missing R3, L3, and select
|
||||
@@ -185,10 +208,10 @@ static float L3_Y;
|
||||
|
||||
[self drawTriggers];
|
||||
[self drawStartSelect];
|
||||
[self drawL3R3];
|
||||
[self hideButtons];
|
||||
[self hideBumpers];
|
||||
[self hideSticks];
|
||||
[self hideL3R3];
|
||||
break;
|
||||
case OnScreenControlsLevelFull:
|
||||
[self setupComplexControls];
|
||||
@@ -201,18 +224,23 @@ static float L3_Y;
|
||||
[self hideL3R3]; // Full controls don't need these they have the sticks
|
||||
break;
|
||||
default:
|
||||
NSLog(@"Unknown on-screen controls level: %d", (int)_level);
|
||||
Log(LOG_W, @"Unknown on-screen controls level: %d", (int)_level);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) setupEdgeDetection {
|
||||
_edge.frame = CGRectMake(0, 0, _view.frame.size.width * EDGE_WIDTH, _view.frame.size.height);
|
||||
[_view.layer addSublayer:_edge];
|
||||
}
|
||||
|
||||
// For GCExtendedGamepad controls we move start, select, L3, and R3 to the button
|
||||
- (void) setupExtendedGamepadControls {
|
||||
// Start with the default complex layout
|
||||
[self setupComplexControls];
|
||||
|
||||
START_Y = _view.frame.size.height * .9;
|
||||
SELECT_Y = _view.frame.size.height * .9;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
}
|
||||
|
||||
// For GCGamepad controls we move triggers, start, and select
|
||||
@@ -221,7 +249,42 @@ static float L3_Y;
|
||||
// Start with the default complex layout
|
||||
[self setupComplexControls];
|
||||
|
||||
// TODO
|
||||
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
L2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
|
||||
R2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
|
||||
if (_iPad) {
|
||||
START_X = _controlArea.size.width * .75 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .25 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
// The analog sticks are kept closer to the sides on iPad
|
||||
LS_CENTER_X = _controlArea.size.width * .18 + _controlArea.origin.x;
|
||||
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
RS_CENTER_X = _controlArea.size.width * .82 + _controlArea.origin.x;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
} else {
|
||||
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
LS_CENTER_X = _controlArea.size.width * .25 + _controlArea.origin.x;
|
||||
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
RS_CENTER_X = _controlArea.size.width * .75 + _controlArea.origin.x;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
}
|
||||
}
|
||||
|
||||
// For simple controls we move the triggers and buttons to the bottom
|
||||
@@ -229,154 +292,192 @@ static float L3_Y;
|
||||
// Start with the default complex layout
|
||||
[self setupComplexControls];
|
||||
|
||||
START_Y = _view.frame.size.height * .9;
|
||||
SELECT_Y = _view.frame.size.height * .9;
|
||||
START_X = _controlArea.size.width * .6 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
SELECT_X = _controlArea.size.width * .4 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
L2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
|
||||
L1_Y = _view.frame.size.height * .75;
|
||||
L2_Y = _view.frame.size.height * .9;
|
||||
R1_Y = _view.frame.size.height * .75;
|
||||
R2_Y = _view.frame.size.height * .9;
|
||||
R2_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
}
|
||||
|
||||
- (void) setupComplexControls
|
||||
{
|
||||
D_PAD_CENTER_X = _view.frame.size.width * .15;
|
||||
D_PAD_CENTER_Y = _view.frame.size.height * .55;
|
||||
BUTTON_CENTER_X = _view.frame.size.width * .85;
|
||||
BUTTON_CENTER_Y = _view.frame.size.height * .55;
|
||||
D_PAD_CENTER_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
D_PAD_CENTER_Y = _controlArea.size.height * .60 + _controlArea.origin.y;
|
||||
BUTTON_CENTER_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
BUTTON_CENTER_Y = _controlArea.size.height * .60 + _controlArea.origin.y;
|
||||
|
||||
LS_CENTER_X = _view.frame.size.width * .35;
|
||||
LS_CENTER_Y = _view.frame.size.height * .75;
|
||||
RS_CENTER_X = _view.frame.size.width * .65;
|
||||
RS_CENTER_Y = _view.frame.size.height * .75;
|
||||
if (_iPad)
|
||||
{
|
||||
// The analog sticks are kept closer to the sides on iPad
|
||||
LS_CENTER_X = _controlArea.size.width * .22 + _controlArea.origin.x;
|
||||
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
RS_CENTER_X = _controlArea.size.width * .77 + _controlArea.origin.x;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
LS_CENTER_X = _controlArea.size.width * .35 + _controlArea.origin.x;
|
||||
LS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
RS_CENTER_X = _controlArea.size.width * .65 + _controlArea.origin.x;
|
||||
RS_CENTER_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
}
|
||||
|
||||
START_X = _view.frame.size.width * .55;
|
||||
START_Y = _view.frame.size.height * .1;
|
||||
SELECT_X = _view.frame.size.width * .45;
|
||||
SELECT_Y = _view.frame.size.height * .1;
|
||||
START_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
START_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
SELECT_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
SELECT_Y = _controlArea.size.height * .9 + _controlArea.origin.y;
|
||||
|
||||
L1_X = _view.frame.size.width * .15;
|
||||
L1_Y = _view.frame.size.height * .25;
|
||||
L2_X = _view.frame.size.width * .15;
|
||||
L2_Y = _view.frame.size.height * .1;
|
||||
R1_X = _view.frame.size.width * .85;
|
||||
R1_Y = _view.frame.size.height * .25;
|
||||
R2_X = _view.frame.size.width * .85;
|
||||
R2_Y = _view.frame.size.height * .1;
|
||||
L3_X = _view.frame.size.width * .25;
|
||||
L3_Y = _view.frame.size.height * .85;
|
||||
R3_X = _view.frame.size.width * .75;
|
||||
R3_Y = _view.frame.size.height * .85;
|
||||
L1_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
L1_Y = _controlArea.size.height * .27 + _controlArea.origin.y;
|
||||
L2_X = _controlArea.size.width * .1 + _controlArea.origin.x;
|
||||
L2_Y = _controlArea.size.height * .1 + _controlArea.origin.y;
|
||||
R1_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
R1_Y = _controlArea.size.height * .27 + _controlArea.origin.y;
|
||||
R2_X = _controlArea.size.width * .9 + _controlArea.origin.x;
|
||||
R2_Y = _controlArea.size.height * .1 + _controlArea.origin.y;
|
||||
L3_X = _controlArea.size.width * .25 + _controlArea.origin.x;
|
||||
L3_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
R3_X = _controlArea.size.width * .75 + _controlArea.origin.x;
|
||||
R3_Y = _controlArea.size.height * .75 + _controlArea.origin.y;
|
||||
}
|
||||
|
||||
- (void) drawButtons {
|
||||
// create A button
|
||||
_aButton.contents = (id) [UIImage imageNamed:@"AButton"].CGImage;
|
||||
_aButton.frame = CGRectMake(BUTTON_CENTER_X - BUTTON_SIZE / 2, BUTTON_CENTER_Y + BUTTON_DIST, BUTTON_SIZE, BUTTON_SIZE);
|
||||
UIImage* aButtonImage = [UIImage imageNamed:@"AButton"];
|
||||
_aButton.contents = (id) aButtonImage.CGImage;
|
||||
_aButton.frame = CGRectMake(BUTTON_CENTER_X - aButtonImage.size.width / 2, BUTTON_CENTER_Y + BUTTON_DIST, aButtonImage.size.width, aButtonImage.size.height);
|
||||
[_view.layer addSublayer:_aButton];
|
||||
|
||||
|
||||
// create B button
|
||||
_bButton.frame = CGRectMake(BUTTON_CENTER_X + BUTTON_DIST, BUTTON_CENTER_Y - BUTTON_SIZE / 2, BUTTON_SIZE, BUTTON_SIZE);
|
||||
_bButton.contents = (id) [UIImage imageNamed:@"BButton"].CGImage;
|
||||
UIImage* bButtonImage = [UIImage imageNamed:@"BButton"];
|
||||
_bButton.frame = CGRectMake(BUTTON_CENTER_X + BUTTON_DIST, BUTTON_CENTER_Y - bButtonImage.size.height / 2, bButtonImage.size.width, bButtonImage.size.height);
|
||||
_bButton.contents = (id) bButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_bButton];
|
||||
|
||||
|
||||
// create X Button
|
||||
_xButton.frame = CGRectMake(BUTTON_CENTER_X - BUTTON_DIST - BUTTON_SIZE, BUTTON_CENTER_Y - BUTTON_SIZE / 2, BUTTON_SIZE, BUTTON_SIZE);
|
||||
_xButton.contents = (id) [UIImage imageNamed:@"XButton"].CGImage;
|
||||
UIImage* xButtonImage = [UIImage imageNamed:@"XButton"];
|
||||
_xButton.frame = CGRectMake(BUTTON_CENTER_X - BUTTON_DIST - xButtonImage.size.width, BUTTON_CENTER_Y - xButtonImage.size.height/ 2, xButtonImage.size.width, xButtonImage.size.height);
|
||||
_xButton.contents = (id) xButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_xButton];
|
||||
|
||||
|
||||
// create Y Button
|
||||
_yButton.frame = CGRectMake(BUTTON_CENTER_X - BUTTON_SIZE / 2, BUTTON_CENTER_Y - BUTTON_DIST - BUTTON_SIZE, BUTTON_SIZE, BUTTON_SIZE);
|
||||
_yButton.contents = (id) [UIImage imageNamed:@"YButton"].CGImage;
|
||||
UIImage* yButtonImage = [UIImage imageNamed:@"YButton"];
|
||||
_yButton.frame = CGRectMake(BUTTON_CENTER_X - yButtonImage.size.width / 2, BUTTON_CENTER_Y - BUTTON_DIST - yButtonImage.size.height, yButtonImage.size.width, yButtonImage.size.height);
|
||||
_yButton.contents = (id) yButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_yButton];
|
||||
|
||||
|
||||
// create Down button
|
||||
_downButton.frame = CGRectMake(D_PAD_CENTER_X - D_PAD_SHORT / 2, D_PAD_CENTER_Y + D_PAD_DIST, D_PAD_SHORT, D_PAD_LONG);
|
||||
_downButton.contents = (id) [UIImage imageNamed:@"DownButton"].CGImage;
|
||||
UIImage* downButtonImage = [UIImage imageNamed:@"DownButton"];
|
||||
_downButton.frame = CGRectMake(D_PAD_CENTER_X - downButtonImage.size.width / 2, D_PAD_CENTER_Y + D_PAD_DIST, downButtonImage.size.width, downButtonImage.size.height);
|
||||
_downButton.contents = (id) downButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_downButton];
|
||||
|
||||
|
||||
// create Right button
|
||||
_rightButton.frame = CGRectMake(D_PAD_CENTER_X + D_PAD_DIST, D_PAD_CENTER_Y - D_PAD_SHORT / 2, D_PAD_LONG, D_PAD_SHORT);
|
||||
_rightButton.contents = (id) [UIImage imageNamed:@"RightButton"].CGImage;
|
||||
UIImage* rightButtonImage = [UIImage imageNamed:@"RightButton"];
|
||||
_rightButton.frame = CGRectMake(D_PAD_CENTER_X + D_PAD_DIST, D_PAD_CENTER_Y - rightButtonImage.size.height / 2, rightButtonImage.size.width, rightButtonImage.size.height);
|
||||
_rightButton.contents = (id) rightButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_rightButton];
|
||||
|
||||
|
||||
// create Up button
|
||||
_upButton.frame = CGRectMake(D_PAD_CENTER_X - D_PAD_SHORT / 2, D_PAD_CENTER_Y - D_PAD_DIST - D_PAD_LONG, D_PAD_SHORT, D_PAD_LONG);
|
||||
_upButton.contents = (id) [UIImage imageNamed:@"UpButton"].CGImage;
|
||||
UIImage* upButtonImage = [UIImage imageNamed:@"UpButton"];
|
||||
_upButton.frame = CGRectMake(D_PAD_CENTER_X - upButtonImage.size.width / 2, D_PAD_CENTER_Y - D_PAD_DIST - upButtonImage.size.height, upButtonImage.size.width, upButtonImage.size.height);
|
||||
_upButton.contents = (id) upButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_upButton];
|
||||
|
||||
|
||||
// create Left button
|
||||
_leftButton.frame = CGRectMake(D_PAD_CENTER_X - D_PAD_DIST - D_PAD_LONG, D_PAD_CENTER_Y - D_PAD_SHORT / 2, D_PAD_LONG, D_PAD_SHORT);
|
||||
_leftButton.contents = (id) [UIImage imageNamed:@"LeftButton"].CGImage;
|
||||
UIImage* leftButtonImage = [UIImage imageNamed:@"LeftButton"];
|
||||
_leftButton.frame = CGRectMake(D_PAD_CENTER_X - D_PAD_DIST - leftButtonImage.size.width, D_PAD_CENTER_Y - leftButtonImage.size.height / 2, leftButtonImage.size.width, leftButtonImage.size.height);
|
||||
_leftButton.contents = (id) leftButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_leftButton];
|
||||
}
|
||||
|
||||
- (void) drawStartSelect {
|
||||
// create Start button
|
||||
_startButton.frame = CGRectMake(START_X - START_WIDTH / 2, START_Y - START_HEIGHT / 2, START_WIDTH, START_HEIGHT);
|
||||
_startButton.contents = (id) [UIImage imageNamed:@"StartButton"].CGImage;
|
||||
UIImage* startButtonImage = [UIImage imageNamed:@"StartButton"];
|
||||
_startButton.frame = CGRectMake(START_X - startButtonImage.size.width / 2, START_Y - startButtonImage.size.height / 2, startButtonImage.size.width, startButtonImage.size.height);
|
||||
_startButton.contents = (id) startButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_startButton];
|
||||
|
||||
|
||||
// create Select button
|
||||
_selectButton.frame = CGRectMake(SELECT_X - SELECT_WIDTH / 2, SELECT_Y - SELECT_HEIGHT / 2, SELECT_WIDTH, SELECT_HEIGHT);
|
||||
_selectButton.contents = (id) [UIImage imageNamed:@"SelectButton"].CGImage;
|
||||
UIImage* selectButtonImage = [UIImage imageNamed:@"SelectButton"];
|
||||
_selectButton.frame = CGRectMake(SELECT_X - selectButtonImage.size.width / 2, SELECT_Y - selectButtonImage.size.height / 2, selectButtonImage.size.width, selectButtonImage.size.height);
|
||||
_selectButton.contents = (id) selectButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_selectButton];
|
||||
}
|
||||
|
||||
- (void) drawBumpers {
|
||||
// create L1 button
|
||||
_l1Button.frame = CGRectMake(L1_X - BUMPER_SIZE / 2, L1_Y - BUMPER_SIZE / 2, BUMPER_SIZE, BUMPER_SIZE);
|
||||
_l1Button.contents = (id) [UIImage imageNamed:@"L1"].CGImage;
|
||||
UIImage* l1ButtonImage = [UIImage imageNamed:@"L1"];
|
||||
_l1Button.frame = CGRectMake(L1_X - l1ButtonImage.size.width / 2, L1_Y - l1ButtonImage.size.height / 2, l1ButtonImage.size.width, l1ButtonImage.size.height);
|
||||
_l1Button.contents = (id) l1ButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_l1Button];
|
||||
|
||||
// create R1 button
|
||||
_r1Button.frame = CGRectMake(R1_X - BUMPER_SIZE / 2, R1_Y - BUMPER_SIZE / 2, BUMPER_SIZE, BUMPER_SIZE);
|
||||
_r1Button.contents = (id) [UIImage imageNamed:@"R1"].CGImage;
|
||||
UIImage* r1ButtonImage = [UIImage imageNamed:@"R1"];
|
||||
_r1Button.frame = CGRectMake(R1_X - r1ButtonImage.size.width / 2, R1_Y - r1ButtonImage.size.height / 2, r1ButtonImage.size.width, r1ButtonImage.size.height);
|
||||
_r1Button.contents = (id) r1ButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_r1Button];
|
||||
}
|
||||
|
||||
- (void) drawTriggers {
|
||||
// create L2 button
|
||||
_l2Button.frame = CGRectMake(L2_X - TRIGGER_SIZE / 2, L2_Y - TRIGGER_SIZE / 2, TRIGGER_SIZE, TRIGGER_SIZE);
|
||||
_l2Button.contents = (id) [UIImage imageNamed:@"L2"].CGImage;
|
||||
UIImage* l2ButtonImage = [UIImage imageNamed:@"L2"];
|
||||
_l2Button.frame = CGRectMake(L2_X - l2ButtonImage.size.width / 2, L2_Y - l2ButtonImage.size.height / 2, l2ButtonImage.size.width, l2ButtonImage.size.height);
|
||||
_l2Button.contents = (id) l2ButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_l2Button];
|
||||
|
||||
// create R2 button
|
||||
_r2Button.frame = CGRectMake(R2_X - TRIGGER_SIZE / 2, R2_Y - TRIGGER_SIZE / 2, TRIGGER_SIZE, TRIGGER_SIZE);
|
||||
_r2Button.contents = (id) [UIImage imageNamed:@"R2"].CGImage;
|
||||
UIImage* r2ButtonImage = [UIImage imageNamed:@"R2"];
|
||||
_r2Button.frame = CGRectMake(R2_X - r2ButtonImage.size.width / 2, R2_Y - r2ButtonImage.size.height / 2, r2ButtonImage.size.width, r2ButtonImage.size.height);
|
||||
_r2Button.contents = (id) r2ButtonImage.CGImage;
|
||||
[_view.layer addSublayer:_r2Button];
|
||||
}
|
||||
|
||||
- (void) drawSticks {
|
||||
// create left analog stick
|
||||
_leftStickBackground.frame = CGRectMake(LS_CENTER_X - STICK_OUTER_SIZE / 2, LS_CENTER_Y - STICK_OUTER_SIZE / 2, STICK_OUTER_SIZE, STICK_OUTER_SIZE);
|
||||
_leftStickBackground.contents = (id) [UIImage imageNamed:@"StickOuter"].CGImage;
|
||||
UIImage* leftStickBgImage = [UIImage imageNamed:@"StickOuter"];
|
||||
_leftStickBackground.frame = CGRectMake(LS_CENTER_X - leftStickBgImage.size.width / 2, LS_CENTER_Y - leftStickBgImage.size.height / 2, leftStickBgImage.size.width, leftStickBgImage.size.height);
|
||||
_leftStickBackground.contents = (id) leftStickBgImage.CGImage;
|
||||
[_view.layer addSublayer:_leftStickBackground];
|
||||
|
||||
_leftStick.frame = CGRectMake(LS_CENTER_X - STICK_INNER_SIZE / 2, LS_CENTER_Y - STICK_INNER_SIZE / 2, STICK_INNER_SIZE, STICK_INNER_SIZE);
|
||||
_leftStick.contents = (id) [UIImage imageNamed:@"StickInner"].CGImage;
|
||||
UIImage* leftStickImage = [UIImage imageNamed:@"StickInner"];
|
||||
_leftStick.frame = CGRectMake(LS_CENTER_X - leftStickImage.size.width / 2, LS_CENTER_Y - leftStickImage.size.height / 2, leftStickImage.size.width, leftStickImage.size.height);
|
||||
_leftStick.contents = (id) leftStickImage.CGImage;
|
||||
[_view.layer addSublayer:_leftStick];
|
||||
|
||||
|
||||
// create right analog stick
|
||||
_rightStickBackground.frame = CGRectMake(RS_CENTER_X - STICK_OUTER_SIZE / 2, RS_CENTER_Y - STICK_OUTER_SIZE / 2, STICK_OUTER_SIZE, STICK_OUTER_SIZE);
|
||||
_rightStickBackground.contents = (id) [UIImage imageNamed:@"StickOuter"].CGImage;
|
||||
UIImage* rightStickBgImage = [UIImage imageNamed:@"StickOuter"];
|
||||
_rightStickBackground.frame = CGRectMake(RS_CENTER_X - rightStickBgImage.size.width / 2, RS_CENTER_Y - rightStickBgImage.size.height / 2, rightStickBgImage.size.width, rightStickBgImage.size.height);
|
||||
_rightStickBackground.contents = (id) rightStickBgImage.CGImage;
|
||||
[_view.layer addSublayer:_rightStickBackground];
|
||||
|
||||
_rightStick.frame = CGRectMake(RS_CENTER_X - STICK_INNER_SIZE / 2, RS_CENTER_Y - STICK_INNER_SIZE / 2, STICK_INNER_SIZE, STICK_INNER_SIZE);
|
||||
_rightStick.contents = (id) [UIImage imageNamed:@"StickInner"].CGImage;
|
||||
UIImage* rightStickImage = [UIImage imageNamed:@"StickInner"];
|
||||
_rightStick.frame = CGRectMake(RS_CENTER_X - rightStickImage.size.width / 2, RS_CENTER_Y - rightStickImage.size.height / 2, rightStickImage.size.width, rightStickImage.size.height);
|
||||
_rightStick.contents = (id) rightStickImage.CGImage;
|
||||
[_view.layer addSublayer:_rightStick];
|
||||
|
||||
STICK_INNER_SIZE = rightStickImage.size.width;
|
||||
STICK_OUTER_SIZE = rightStickBgImage.size.width;
|
||||
}
|
||||
|
||||
- (void) drawL3R3 {
|
||||
_l3Button.frame = CGRectMake(L3_X - L3R3_SIZE / 2, L3_Y - L3R3_SIZE / 2, L3R3_SIZE, L3R3_SIZE);
|
||||
_l3Button.contents = (id) [UIImage imageNamed:@"L3"].CGImage;
|
||||
_l3Button.cornerRadius = L3R3_SIZE / 2;
|
||||
UIImage* l3ButtonImage = [UIImage imageNamed:@"L3"];
|
||||
_l3Button.frame = CGRectMake(L3_X - l3ButtonImage.size.width / 2, L3_Y - l3ButtonImage.size.height / 2, l3ButtonImage.size.width, l3ButtonImage.size.height);
|
||||
_l3Button.contents = (id) l3ButtonImage.CGImage;
|
||||
_l3Button.cornerRadius = l3ButtonImage.size.width / 2;
|
||||
_l3Button.borderColor = [UIColor colorWithRed:15.f/255 green:160.f/255 blue:40.f/255 alpha:1.f].CGColor;
|
||||
[_view.layer addSublayer:_l3Button];
|
||||
|
||||
_r3Button.frame = CGRectMake(R3_X - L3R3_SIZE / 2, R3_Y - L3R3_SIZE / 2, L3R3_SIZE, L3R3_SIZE);
|
||||
_r3Button.contents = (id) [UIImage imageNamed:@"R3"].CGImage;
|
||||
_r3Button.cornerRadius = L3R3_SIZE / 2;
|
||||
UIImage* r3ButtonImage = [UIImage imageNamed:@"R3"];
|
||||
_r3Button.frame = CGRectMake(R3_X - r3ButtonImage.size.width / 2, R3_Y - r3ButtonImage.size.height / 2, r3ButtonImage.size.width, r3ButtonImage.size.height);
|
||||
_r3Button.contents = (id) r3ButtonImage.CGImage;
|
||||
_r3Button.cornerRadius = r3ButtonImage.size.width / 2;
|
||||
_r3Button.borderColor = [UIColor colorWithRed:15.f/255 green:160.f/255 blue:40.f/255 alpha:1.f].CGColor;
|
||||
[_view.layer addSublayer:_r3Button];
|
||||
}
|
||||
@@ -430,7 +531,7 @@ static float L3_Y;
|
||||
float lsMaxY = LS_CENTER_Y + STICK_OUTER_SIZE / 2;
|
||||
float lsMinX = LS_CENTER_X - STICK_OUTER_SIZE / 2;
|
||||
float lsMinY = LS_CENTER_Y - STICK_OUTER_SIZE / 2;
|
||||
|
||||
|
||||
for (UITouch* touch in touches) {
|
||||
CGPoint touchLocation = [touch locationInView:_view];
|
||||
float xLoc = touchLocation.x;
|
||||
@@ -440,34 +541,34 @@ static float L3_Y;
|
||||
if (xLoc < lsMinX) xLoc = lsMinX;
|
||||
if (yLoc > lsMaxY) yLoc = lsMaxY;
|
||||
if (yLoc < lsMinY) yLoc = lsMinY;
|
||||
|
||||
|
||||
_leftStick.frame = CGRectMake(xLoc - STICK_INNER_SIZE / 2, yLoc - STICK_INNER_SIZE / 2, STICK_INNER_SIZE, STICK_INNER_SIZE);
|
||||
|
||||
|
||||
float xStickVal = (xLoc - LS_CENTER_X) / (lsMaxX - LS_CENTER_X);
|
||||
float yStickVal = (yLoc - LS_CENTER_Y) / (lsMaxY - LS_CENTER_Y);
|
||||
|
||||
|
||||
if (fabsf(xStickVal) < STICK_DEAD_ZONE) xStickVal = 0;
|
||||
if (fabsf(yStickVal) < STICK_DEAD_ZONE) yStickVal = 0;
|
||||
|
||||
[_controllerSupport updateLeftStick:0x7FFE * xStickVal y:0x7FFE * -yStickVal];
|
||||
|
||||
|
||||
[_controllerSupport updateLeftStick:_controller x:0x7FFE * xStickVal y:0x7FFE * -yStickVal];
|
||||
|
||||
updated = true;
|
||||
} else if (touch == _rsTouch) {
|
||||
if (xLoc > rsMaxX) xLoc = rsMaxX;
|
||||
if (xLoc < rsMinX) xLoc = rsMinX;
|
||||
if (yLoc > rsMaxY) yLoc = rsMaxY;
|
||||
if (yLoc < rsMinY) yLoc = rsMinY;
|
||||
|
||||
|
||||
_rightStick.frame = CGRectMake(xLoc - STICK_INNER_SIZE / 2, yLoc - STICK_INNER_SIZE / 2, STICK_INNER_SIZE, STICK_INNER_SIZE);
|
||||
|
||||
|
||||
float xStickVal = (xLoc - RS_CENTER_X) / (rsMaxX - RS_CENTER_X);
|
||||
float yStickVal = (yLoc - RS_CENTER_Y) / (rsMaxY - RS_CENTER_Y);
|
||||
|
||||
|
||||
if (fabsf(xStickVal) < STICK_DEAD_ZONE) xStickVal = 0;
|
||||
if (fabsf(yStickVal) < STICK_DEAD_ZONE) yStickVal = 0;
|
||||
|
||||
[_controllerSupport updateRightStick:0x7FFE * xStickVal y:0x7FFE * -yStickVal];
|
||||
|
||||
|
||||
[_controllerSupport updateRightStick:_controller x:0x7FFE * xStickVal y:0x7FFE * -yStickVal];
|
||||
|
||||
updated = true;
|
||||
} else if (touch == _aTouch) {
|
||||
buttonTouch = true;
|
||||
@@ -502,11 +603,14 @@ static float L3_Y;
|
||||
} else if (touch == _r3Touch) {
|
||||
buttonTouch = true;
|
||||
}
|
||||
if ([_deadTouches containsObject:touch]) {
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[_controllerSupport updateFinished];
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
}
|
||||
return updated || buttonTouch;
|
||||
return updated || buttonTouch;
|
||||
}
|
||||
|
||||
- (BOOL)handleTouchDownEvent:touches {
|
||||
@@ -514,69 +618,69 @@ static float L3_Y;
|
||||
BOOL stickTouch = false;
|
||||
for (UITouch* touch in touches) {
|
||||
CGPoint touchLocation = [touch locationInView:_view];
|
||||
|
||||
|
||||
if ([_aButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:A_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:A_FLAG];
|
||||
_aTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_bButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:B_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:B_FLAG];
|
||||
_bTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_xButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:X_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:X_FLAG];
|
||||
_xTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_yButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:Y_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:Y_FLAG];
|
||||
_yTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_upButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:UP_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:UP_FLAG];
|
||||
_upTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_downButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:DOWN_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:DOWN_FLAG];
|
||||
_downTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_leftButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:LEFT_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:LEFT_FLAG];
|
||||
_leftTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_rightButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:RIGHT_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:RIGHT_FLAG];
|
||||
_rightTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_startButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:PLAY_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:PLAY_FLAG];
|
||||
_startTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_selectButton.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:BACK_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:BACK_FLAG];
|
||||
_selectTouch = touch;
|
||||
updated = true;
|
||||
} else if ([_l1Button.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:LB_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:LB_FLAG];
|
||||
_l1Touch = touch;
|
||||
updated = true;
|
||||
} else if ([_r1Button.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport setButtonFlag:RB_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:RB_FLAG];
|
||||
_r1Touch = touch;
|
||||
updated = true;
|
||||
} else if ([_l2Button.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport updateLeftTrigger:0xFF];
|
||||
[_controllerSupport updateLeftTrigger:_controller left:0xFF];
|
||||
_l2Touch = touch;
|
||||
updated = true;
|
||||
} else if ([_r2Button.presentationLayer hitTest:touchLocation]) {
|
||||
[_controllerSupport updateRightTrigger:0xFF];
|
||||
[_controllerSupport updateRightTrigger:_controller right:0xFF];
|
||||
_r2Touch = touch;
|
||||
updated = true;
|
||||
} else if ([_l3Button.presentationLayer hitTest:touchLocation]) {
|
||||
if (l3Set) {
|
||||
[_controllerSupport clearButtonFlag:LS_CLK_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:LS_CLK_FLAG];
|
||||
_l3Button.borderWidth = 0.0f;
|
||||
} else {
|
||||
[_controllerSupport setButtonFlag:LS_CLK_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:LS_CLK_FLAG];
|
||||
_l3Button.borderWidth = 2.0f;
|
||||
}
|
||||
l3Set = !l3Set;
|
||||
@@ -584,10 +688,10 @@ static float L3_Y;
|
||||
updated = true;
|
||||
} else if ([_r3Button.presentationLayer hitTest:touchLocation]) {
|
||||
if (r3Set) {
|
||||
[_controllerSupport clearButtonFlag:RS_CLK_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:RS_CLK_FLAG];
|
||||
_r3Button.borderWidth = 0.0f;
|
||||
} else {
|
||||
[_controllerSupport setButtonFlag:RS_CLK_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:RS_CLK_FLAG];
|
||||
_r3Button.borderWidth = 2.0f;
|
||||
}
|
||||
r3Set = !r3Set;
|
||||
@@ -599,7 +703,7 @@ static float L3_Y;
|
||||
// Use (-) modifier to conversion since receiver is earlier than now
|
||||
double l3TouchTime = [l3TouchStart timeIntervalSinceNow] * -1000.0;
|
||||
if (l3TouchTime < STICK_CLICK_RATE) {
|
||||
[_controllerSupport setButtonFlag:LS_CLK_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:LS_CLK_FLAG];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
@@ -611,16 +715,22 @@ static float L3_Y;
|
||||
// Use (-) modifier to conversion since receiver is earlier than now
|
||||
double r3TouchTime = [r3TouchStart timeIntervalSinceNow] * -1000.0;
|
||||
if (r3TouchTime < STICK_CLICK_RATE) {
|
||||
[_controllerSupport setButtonFlag:RS_CLK_FLAG];
|
||||
[_controllerSupport setButtonFlag:_controller flags:RS_CLK_FLAG];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
_rsTouch = touch;
|
||||
stickTouch = true;
|
||||
} else if ([_edge.presentationLayer hitTest:touchLocation]) {
|
||||
_edgeTouch = touch;
|
||||
}
|
||||
if (!updated && !stickTouch && [self isInDeadZone:touch]) {
|
||||
[_deadTouches addObject:touch];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[_controllerSupport updateFinished];
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
}
|
||||
return updated || stickTouch;
|
||||
}
|
||||
@@ -630,72 +740,72 @@ static float L3_Y;
|
||||
BOOL touched = false;
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch == _aTouch) {
|
||||
[_controllerSupport clearButtonFlag:A_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:A_FLAG];
|
||||
_aTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _bTouch) {
|
||||
[_controllerSupport clearButtonFlag:B_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:B_FLAG];
|
||||
_bTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _xTouch) {
|
||||
[_controllerSupport clearButtonFlag:X_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:X_FLAG];
|
||||
_xTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _yTouch) {
|
||||
[_controllerSupport clearButtonFlag:Y_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:Y_FLAG];
|
||||
_yTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _upTouch) {
|
||||
[_controllerSupport clearButtonFlag:UP_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:UP_FLAG];
|
||||
_upTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _downTouch) {
|
||||
[_controllerSupport clearButtonFlag:DOWN_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:DOWN_FLAG];
|
||||
_downTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _leftTouch) {
|
||||
[_controllerSupport clearButtonFlag:LEFT_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:LEFT_FLAG];
|
||||
_leftTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _rightTouch) {
|
||||
[_controllerSupport clearButtonFlag:RIGHT_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:RIGHT_FLAG];
|
||||
_rightTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _startTouch) {
|
||||
[_controllerSupport clearButtonFlag:PLAY_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:PLAY_FLAG];
|
||||
_startTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _selectTouch) {
|
||||
[_controllerSupport clearButtonFlag:BACK_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:BACK_FLAG];
|
||||
_selectTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _l1Touch) {
|
||||
[_controllerSupport clearButtonFlag:LB_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:LB_FLAG];
|
||||
_l1Touch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _r1Touch) {
|
||||
[_controllerSupport clearButtonFlag:RB_FLAG];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:RB_FLAG];
|
||||
_r1Touch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _l2Touch) {
|
||||
[_controllerSupport updateLeftTrigger:0];
|
||||
[_controllerSupport updateLeftTrigger:_controller left:0];
|
||||
_l2Touch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _r2Touch) {
|
||||
[_controllerSupport updateRightTrigger:0];
|
||||
[_controllerSupport updateRightTrigger:_controller right:0];
|
||||
_r2Touch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _lsTouch) {
|
||||
_leftStick.frame = CGRectMake(LS_CENTER_X - STICK_INNER_SIZE / 2, LS_CENTER_Y - STICK_INNER_SIZE / 2, STICK_INNER_SIZE, STICK_INNER_SIZE);
|
||||
[_controllerSupport updateLeftStick:0 y:0];
|
||||
[_controllerSupport clearButtonFlag:LS_CLK_FLAG];
|
||||
[_controllerSupport updateLeftStick:_controller x:0 y:0];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:LS_CLK_FLAG];
|
||||
l3TouchStart = [NSDate date];
|
||||
_lsTouch = nil;
|
||||
updated = true;
|
||||
} else if (touch == _rsTouch) {
|
||||
_rightStick.frame = CGRectMake(RS_CENTER_X - STICK_INNER_SIZE / 2, RS_CENTER_Y - STICK_INNER_SIZE / 2, STICK_INNER_SIZE, STICK_INNER_SIZE);
|
||||
[_controllerSupport updateRightStick:0 y:0];
|
||||
[_controllerSupport clearButtonFlag:RS_CLK_FLAG];
|
||||
[_controllerSupport updateRightStick:_controller x:0 y:0];
|
||||
[_controllerSupport clearButtonFlag:_controller flags:RS_CLK_FLAG];
|
||||
r3TouchStart = [NSDate date];
|
||||
_rsTouch = nil;
|
||||
updated = true;
|
||||
@@ -707,12 +817,125 @@ static float L3_Y;
|
||||
else if (touch == _r3Touch) {
|
||||
_r3Touch = nil;
|
||||
touched = true;
|
||||
} else if (touch == _edgeTouch) {
|
||||
_edgeTouch = nil;
|
||||
if ([touch locationInView:_view].x >= _view.frame.size.width / 4) {
|
||||
[_edgeDelegate edgeSwiped];
|
||||
}
|
||||
}
|
||||
if ([_deadTouches containsObject:touch]) {
|
||||
[_deadTouches removeObject:touch];
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated) {
|
||||
[_controllerSupport updateFinished];
|
||||
[_controllerSupport updateFinished:_controller];
|
||||
}
|
||||
|
||||
return updated || touched;
|
||||
}
|
||||
|
||||
- (BOOL) isInDeadZone:(UITouch*) touch {
|
||||
switch (_level) {
|
||||
case OnScreenControlsLevelFull:
|
||||
return [self isDpadDeadZone:touch]
|
||||
|| [self isAbxyDeadZone:touch]
|
||||
|| [self isTriggerDeadZone:touch]
|
||||
|| [self isBumperDeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch];
|
||||
case OnScreenControlsLevelSimple:
|
||||
return [self isTriggerDeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch]
|
||||
|| [self isL3R3DeadZone:touch];
|
||||
case OnScreenControlsLevelAutoGCExtendedGamepad:
|
||||
return [self isL3R3DeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch];
|
||||
case OnScreenControlsLevelAutoGCGamepad:
|
||||
return [self isTriggerDeadZone:touch]
|
||||
|| [self isStartSelectDeadZone:touch];
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) isDpadDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_upButton.frame.origin.y
|
||||
endX:_rightButton.frame.origin.x + _rightButton.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isAbxyDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_xButton.frame.origin.x
|
||||
startY:_yButton.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isBumperDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_l2Button.frame.origin.y + _l2Button.frame.size.height
|
||||
endX:_l1Button.frame.origin.x + _l1Button.frame.size.width
|
||||
endY:_upButton.frame.origin.y]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_r2Button.frame.origin.x
|
||||
startY:_r2Button.frame.origin.y + _r2Button.frame.size.height
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_yButton.frame.origin.y];
|
||||
}
|
||||
|
||||
- (BOOL) isTriggerDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_l2Button.frame.origin.y
|
||||
endX:_l2Button.frame.origin.x + _l2Button.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_r2Button.frame.origin.x
|
||||
startY:_r2Button.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isL3R3DeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_l3Button.frame.origin.y
|
||||
endX:_view.frame.origin.x
|
||||
endY:_view.frame.origin.y + _view.frame.size.height]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_r3Button.frame.origin.x
|
||||
startY:_r3Button.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isStartSelectDeadZone:(UITouch*) touch {
|
||||
return [self isDeadZone:touch
|
||||
startX:_startButton.frame.origin.x
|
||||
startY:_startButton.frame.origin.y
|
||||
endX:_view.frame.origin.x + _view.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height]
|
||||
|| [self isDeadZone:touch
|
||||
startX:_view.frame.origin.x
|
||||
startY:_selectButton.frame.origin.y
|
||||
endX:_selectButton.frame.origin.x + _selectButton.frame.size.width
|
||||
endY:_view.frame.origin.y + _view.frame.size.height];
|
||||
}
|
||||
|
||||
- (BOOL) isDeadZone:(UITouch*) touch startX:(float)deadZoneStartX startY:(float)deadZoneStartY endX:(float)deadZoneEndX endY:(float)deadZoneEndY {
|
||||
deadZoneStartX -= DEAD_ZONE_PADDING;
|
||||
deadZoneStartY -= DEAD_ZONE_PADDING;
|
||||
deadZoneEndX += DEAD_ZONE_PADDING;
|
||||
deadZoneEndY += DEAD_ZONE_PADDING;
|
||||
|
||||
CGPoint touchLocation = [touch locationInView:_view];
|
||||
return (touchLocation.x > deadZoneStartX && touchLocation.x < deadZoneEndX
|
||||
&& touchLocation.y > deadZoneStartY && touchLocation.y < deadZoneEndY);
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
//
|
||||
// StreamView.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/19/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "ControllerSupport.h"
|
||||
|
||||
@protocol EdgeDetectionDelegate <NSObject>
|
||||
|
||||
- (void) edgeSwiped;
|
||||
|
||||
@end
|
||||
|
||||
@interface StreamView : UIView
|
||||
|
||||
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport;
|
||||
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate;
|
||||
- (void) setMouseDeltaFactors:(float)x y:(float)y;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// StreamView.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/19/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "StreamView.h"
|
||||
@@ -29,22 +29,22 @@
|
||||
screenFactor = [[UIScreen mainScreen] scale];
|
||||
}
|
||||
|
||||
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport {
|
||||
onScreenControls = [[OnScreenControls alloc] initWithView:self controllerSup:controllerSupport];
|
||||
- (void) setupOnScreenControls:(ControllerSupport*)controllerSupport swipeDelegate:(id<EdgeDetectionDelegate>)swipeDelegate {
|
||||
onScreenControls = [[OnScreenControls alloc] initWithView:self controllerSup:controllerSupport swipeDelegate:swipeDelegate];
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
OnScreenControlsLevel level = (OnScreenControlsLevel)[[dataMan retrieveSettings].onscreenControls integerValue];
|
||||
OnScreenControlsLevel level = (OnScreenControlsLevel)[[dataMan getSettings].onscreenControls integerValue];
|
||||
|
||||
if (level == OnScreenControlsLevelAuto) {
|
||||
[controllerSupport initAutoOnScreenControlMode:onScreenControls];
|
||||
}
|
||||
else {
|
||||
NSLog(@"Setting manual on-screen controls level: %d", (int)level);
|
||||
Log(LOG_I, @"Setting manual on-screen controls level: %d", (int)level);
|
||||
[onScreenControls setLevel:level];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
NSLog(@"Touch down");
|
||||
Log(LOG_D, @"Touch down");
|
||||
if (![onScreenControls handleTouchDownEvent:touches]) {
|
||||
UITouch *touch = [[event allTouches] anyObject];
|
||||
touchLocation = [touch locationInView:self];
|
||||
@@ -67,15 +67,16 @@
|
||||
deltaX *= xDeltaFactor * screenFactor;
|
||||
deltaY *= yDeltaFactor * screenFactor;
|
||||
|
||||
LiSendMouseMoveEvent(deltaX, deltaY);
|
||||
|
||||
touchMoved = true;
|
||||
touchLocation = currentLocation;
|
||||
if (deltaX != 0 || deltaY != 0) {
|
||||
LiSendMouseMoveEvent(deltaX, deltaY);
|
||||
touchMoved = true;
|
||||
touchLocation = currentLocation;
|
||||
}
|
||||
}
|
||||
} else if ([[event allTouches] count] == 2) {
|
||||
CGPoint firstLocation = [[[[event allTouches] allObjects] objectAtIndex:0] locationInView:self];
|
||||
CGPoint secondLocation = [[[[event allTouches] allObjects] objectAtIndex:1] locationInView:self];
|
||||
|
||||
|
||||
CGPoint avgLocation = CGPointMake((firstLocation.x + secondLocation.x) / 2, (firstLocation.y + secondLocation.y) / 2);
|
||||
if (touchLocation.y != avgLocation.y) {
|
||||
LiSendScrollEvent(avgLocation.y - touchLocation.y);
|
||||
@@ -88,28 +89,28 @@
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
|
||||
NSLog(@"Touch up");
|
||||
Log(LOG_D, @"Touch up");
|
||||
if (![onScreenControls handleTouchUpEvent:touches]) {
|
||||
if (!touchMoved) {
|
||||
if ([[event allTouches] count] == 2) {
|
||||
NSLog(@"Sending right mouse button press");
|
||||
|
||||
Log(LOG_D, @"Sending right mouse button press");
|
||||
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
|
||||
|
||||
|
||||
// Wait 100 ms to simulate a real button press
|
||||
usleep(100 * 1000);
|
||||
|
||||
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
|
||||
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
NSLog(@"Sending left mouse button press");
|
||||
|
||||
Log(LOG_D, @"Sending left mouse button press");
|
||||
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
|
||||
|
||||
|
||||
// Wait 100 ms to simulate a real button press
|
||||
usleep(100 * 1000);
|
||||
|
||||
|
||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.limelight-stream.$(PRODUCT_NAME:rfc1034identifier)</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.3.2</string>
|
||||
<string>1.0.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@@ -26,6 +26,11 @@
|
||||
<string>public.app-category.games</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Roboto-Black.ttf</string>
|
||||
@@ -45,9 +50,9 @@
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<key>UIStatusBarHidden~ipad</key>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<true/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
@@ -59,5 +64,7 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -14,4 +14,5 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#endif
|
||||
#import "Logger.h"
|
||||
#endif
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Limelight 0.3.1.xcdatamodel</string>
|
||||
<string>Moonlight v1.0-2.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Automatic">
|
||||
<entity name="App" representedClassName="App" syncable="YES">
|
||||
<attribute name="id" attributeType="String" syncable="YES"/>
|
||||
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" 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">
|
||||
<attribute name="address" attributeType="String" syncable="YES"/>
|
||||
<attribute name="externalAddress" 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" 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">
|
||||
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" syncable="YES"/>
|
||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" syncable="YES"/>
|
||||
<attribute name="height" attributeType="Integer 32" defaultValueString="720" syncable="YES"/>
|
||||
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
|
||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" 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="163"/>
|
||||
<element name="Settings" positionX="0" positionY="0" width="128" height="135"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="9057" systemVersion="15B42" minimumToolsVersion="Automatic">
|
||||
<entity name="App" representedClassName="App" syncable="YES">
|
||||
<attribute name="id" attributeType="String" syncable="YES"/>
|
||||
<attribute name="image" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES" syncable="YES"/>
|
||||
<attribute name="name" optional="YES" 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">
|
||||
<attribute name="address" attributeType="String" syncable="YES"/>
|
||||
<attribute name="externalAddress" 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" 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">
|
||||
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" syncable="YES"/>
|
||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" syncable="YES"/>
|
||||
<attribute name="height" attributeType="Integer 32" defaultValueString="720" syncable="YES"/>
|
||||
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" syncable="YES"/>
|
||||
<attribute name="uniqueId" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" 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="163"/>
|
||||
<element name="Settings" positionX="0" positionY="0" width="128" height="135"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// AppManager.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/25/14.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TemporaryApp.h"
|
||||
#import "HttpManager.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
@protocol AppAssetCallback <NSObject>
|
||||
|
||||
- (void) receivedAssetForApp:(TemporaryApp*)app;
|
||||
|
||||
@end
|
||||
|
||||
@interface AppAssetManager : NSObject
|
||||
|
||||
- (id) initWithCallback:(id<AppAssetCallback>)callback;
|
||||
- (void) retrieveAssetsFromHost:(TemporaryHost*)host;
|
||||
- (void) stopRetrieving;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// AppManager.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/25/14.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppAssetManager.h"
|
||||
#import "CryptoManager.h"
|
||||
#import "Utils.h"
|
||||
#import "HttpResponse.h"
|
||||
#import "AppAssetRetriever.h"
|
||||
|
||||
@implementation AppAssetManager {
|
||||
NSOperationQueue* _opQueue;
|
||||
id<AppAssetCallback> _callback;
|
||||
}
|
||||
|
||||
static const int MAX_REQUEST_COUNT = 4;
|
||||
|
||||
- (id) initWithCallback:(id<AppAssetCallback>)callback {
|
||||
self = [super init];
|
||||
_callback = callback;
|
||||
_opQueue = [[NSOperationQueue alloc] init];
|
||||
[_opQueue setMaxConcurrentOperationCount:MAX_REQUEST_COUNT];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) retrieveAssetsFromHost:(TemporaryHost*)host {
|
||||
for (TemporaryApp* app in host.appList) {
|
||||
if (app.image == nil) {
|
||||
AppAssetRetriever* retriever = [[AppAssetRetriever alloc] init];
|
||||
retriever.app = app;
|
||||
retriever.host = host;
|
||||
retriever.callback = _callback;
|
||||
|
||||
[_opQueue addOperation:retriever];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) stopRetrieving {
|
||||
[_opQueue cancelAllOperations];
|
||||
}
|
||||
|
||||
- (void) sendCallBackForApp:(TemporaryApp*)app {
|
||||
[_callback receivedAssetForApp:app];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// AppAssetResponse.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HttpResponse.h"
|
||||
|
||||
@interface AppAssetResponse : NSObject <Response>
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// AppAssetResponse.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppAssetResponse.h"
|
||||
|
||||
@implementation AppAssetResponse
|
||||
@synthesize data, statusCode, statusMessage;
|
||||
|
||||
- (void)populateWithData:(NSData *)imageData {
|
||||
self.data = imageData;
|
||||
self.statusMessage = @"App asset has no status message";
|
||||
self.statusCode = -1;
|
||||
}
|
||||
|
||||
- (UIImage*) getImage {
|
||||
UIImage* appImage = [[UIImage alloc] initWithData:self.data];
|
||||
return appImage;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// AppAssetRetriever.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/31/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "TemporaryHost.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "AppAssetManager.h"
|
||||
|
||||
@interface AppAssetRetriever : NSOperation
|
||||
|
||||
@property (nonatomic) TemporaryHost* host;
|
||||
@property (nonatomic) TemporaryApp* app;
|
||||
@property (nonatomic) id<AppAssetCallback> callback;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// AppAssetRetriever.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/31/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppAssetRetriever.h"
|
||||
#import "HttpManager.h"
|
||||
#import "CryptoManager.h"
|
||||
#import "AppAssetResponse.h"
|
||||
#import "HttpRequest.h"
|
||||
#import "IdManager.h"
|
||||
|
||||
@implementation AppAssetRetriever
|
||||
static const double RETRY_DELAY = 2; // seconds
|
||||
static const int MAX_ATTEMPTS = 5;
|
||||
|
||||
- (void) main {
|
||||
UIImage* appImage = nil;
|
||||
int attempts = 0;
|
||||
while (![self isCancelled] && appImage == nil && attempts++ < MAX_ATTEMPTS) {
|
||||
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] deviceName:deviceName cert:[CryptoManager readCertFromFile]];
|
||||
AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]];
|
||||
|
||||
appImage = [UIImage imageWithData:appAssetResp.data];
|
||||
self.app.image = UIImagePNGRepresentation(appImage);
|
||||
|
||||
if (![self isCancelled] && appImage == nil) {
|
||||
[NSThread sleepForTimeInterval:RETRY_DELAY];
|
||||
}
|
||||
}
|
||||
[self performSelectorOnMainThread:@selector(sendCallbackForApp:) withObject:self.app waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void) sendCallbackForApp:(TemporaryApp*)app {
|
||||
[self.callback receivedAssetForApp:app];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// AppListResponse.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpResponse.h"
|
||||
|
||||
@interface AppListResponse : NSObject <Response>
|
||||
|
||||
- (void)populateWithData:(NSData *)data;
|
||||
- (NSSet*) getAppList;
|
||||
- (BOOL) isStatusOk;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// AppListResponse.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppListResponse.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "DataManager.h"
|
||||
#import <libxml2/libxml/xmlreader.h>
|
||||
|
||||
@implementation AppListResponse {
|
||||
NSMutableSet* _appList;
|
||||
}
|
||||
@synthesize data, statusCode, statusMessage;
|
||||
|
||||
static const char* TAG_APP = "App";
|
||||
static const char* TAG_APP_TITLE = "AppTitle";
|
||||
static const char* TAG_APP_ID = "ID";
|
||||
static const char* TAG_APP_IS_RUNNING = "IsRunning";
|
||||
|
||||
- (void)populateWithData:(NSData *)xml {
|
||||
self.data = xml;
|
||||
_appList = [[NSMutableSet alloc] init];
|
||||
[self parseData];
|
||||
}
|
||||
|
||||
- (void) parseData {
|
||||
xmlDocPtr docPtr = xmlParseMemory([self.data bytes], (int)[self.data length]);
|
||||
if (docPtr == NULL) {
|
||||
Log(LOG_W, @"An error occured trying to parse xml.");
|
||||
return;
|
||||
}
|
||||
|
||||
xmlNodePtr node = xmlDocGetRootElement(docPtr);
|
||||
if (node == NULL) {
|
||||
Log(LOG_W, @"No root XML element.");
|
||||
xmlFreeDoc(docPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
xmlChar* statusStr = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_CODE UTF8String]);
|
||||
if (statusStr != NULL) {
|
||||
int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue];
|
||||
xmlFree(statusStr);
|
||||
self.statusCode = status;
|
||||
}
|
||||
|
||||
xmlChar* statusMsgXml = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_MESSAGE UTF8String]);
|
||||
NSString* statusMsg;
|
||||
if (statusMsgXml != NULL) {
|
||||
statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml];
|
||||
xmlFree(statusMsgXml);
|
||||
}
|
||||
else {
|
||||
statusMsg = @"Server Error";
|
||||
}
|
||||
self.statusMessage = statusMsg;
|
||||
|
||||
node = node->children;
|
||||
|
||||
while (node != NULL) {
|
||||
//Log(LOG_D, @"node: %s", node->name);
|
||||
if (!xmlStrcmp(node->name, (xmlChar*)TAG_APP)) {
|
||||
xmlNodePtr appInfoNode = node->xmlChildrenNode;
|
||||
NSString* appName = @"";
|
||||
NSString* appId = nil;
|
||||
BOOL appIsRunning = NO;
|
||||
while (appInfoNode != NULL) {
|
||||
if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_TITLE)) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
|
||||
if (nodeVal != NULL) {
|
||||
appName = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else {
|
||||
appName = @"";
|
||||
}
|
||||
} else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_ID)) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
|
||||
if (nodeVal != NULL) {
|
||||
appId = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
}
|
||||
} else if (!xmlStrcmp(appInfoNode->name, (xmlChar*)TAG_APP_IS_RUNNING)) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
|
||||
if (nodeVal != NULL) {
|
||||
appIsRunning = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"];
|
||||
xmlFree(nodeVal);
|
||||
} else {
|
||||
appIsRunning = NO;
|
||||
}
|
||||
}
|
||||
appInfoNode = appInfoNode->next;
|
||||
}
|
||||
if (appId != nil) {
|
||||
TemporaryApp* app = [[TemporaryApp alloc] init];
|
||||
app.name = appName;
|
||||
app.id = appId;
|
||||
app.isRunning = appIsRunning;
|
||||
[_appList addObject:app];
|
||||
}
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
xmlFreeDoc(docPtr);
|
||||
}
|
||||
|
||||
- (NSSet*) getAppList {
|
||||
return _appList;
|
||||
}
|
||||
|
||||
- (BOOL) isStatusOk {
|
||||
return self.statusCode == 200;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// AppManager.h
|
||||
// Limelight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/25/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "App.h"
|
||||
#import "HttpManager.h"
|
||||
#import "Host.h"
|
||||
|
||||
@protocol AppAssetCallback <NSObject>
|
||||
|
||||
- (void) receivedAssetForApp:(App*)app;
|
||||
|
||||
@end
|
||||
|
||||
@interface AppManager : NSObject
|
||||
|
||||
- (id) initWithCallback:(id<AppAssetCallback>)callback;
|
||||
- (void) retrieveAssets:(NSArray*)appList fromHost:(Host*)host;
|
||||
- (void) stopRetrieving;
|
||||
|
||||
@end
|
||||
@@ -1,79 +0,0 @@
|
||||
//
|
||||
// AppManager.m
|
||||
// Limelight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/25/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppManager.h"
|
||||
#import "CryptoManager.h"
|
||||
#import "Utils.h"
|
||||
|
||||
@implementation AppManager {
|
||||
NSOperationQueue* _opQueue;
|
||||
id<AppAssetCallback> _callback;
|
||||
Host* _host;
|
||||
NSString* _uniqueId;
|
||||
NSData* _cert;
|
||||
NSMutableDictionary* _imageCache;
|
||||
}
|
||||
|
||||
- (id) initWithCallback:(id<AppAssetCallback>)callback {
|
||||
self = [super init];
|
||||
_callback = callback;
|
||||
_opQueue = [[NSOperationQueue alloc] init];
|
||||
_imageCache = [[NSMutableDictionary alloc] init];
|
||||
_uniqueId = [CryptoManager getUniqueID];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) retrieveAssets:(NSArray*)appList fromHost:(Host*)host {
|
||||
Host* oldHost = _host;
|
||||
_host = host;
|
||||
BOOL useCache = [oldHost.uuid isEqualToString:_host.uuid];
|
||||
NSLog(@"Using cached app images: %d", useCache);
|
||||
if (!useCache) {
|
||||
[_imageCache removeAllObjects];
|
||||
}
|
||||
for (App* app in appList) {
|
||||
[_opQueue addOperationWithBlock:^{
|
||||
[self retrieveAssetForApp:app useCache:useCache];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) stopRetrieving {
|
||||
[_opQueue cancelAllOperations];
|
||||
}
|
||||
|
||||
- (void) retrieveAssetForApp:(App*)app useCache:(BOOL)useCache {
|
||||
UIImage* appImage = nil;
|
||||
if (useCache) {
|
||||
@synchronized(_imageCache) {
|
||||
UIImage* cachedImage = [_imageCache objectForKey:app.appId];
|
||||
if (cachedImage != nil) {
|
||||
appImage = cachedImage;
|
||||
app.appImage = appImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (appImage == nil) {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* appAsset = [hMan executeRequestSynchronously:[hMan newAppAssetRequestWithAppId:app.appId]];
|
||||
appImage = [UIImage imageWithData:appAsset];
|
||||
app.appImage = appImage;
|
||||
if (appImage != nil) {
|
||||
@synchronized(_imageCache) {
|
||||
[_imageCache setObject:appImage forKey:app.appId];
|
||||
}
|
||||
}
|
||||
}
|
||||
[self performSelectorOnMainThread:@selector(sendCallBackForApp:) withObject:app waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void) sendCallBackForApp:(App*)app {
|
||||
[_callback receivedAssetForApp:app];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,14 +1,14 @@
|
||||
//
|
||||
// DiscoveryManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/1/15.
|
||||
// Copyright (c) 2015 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MDNSManager.h"
|
||||
#import "Host.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
@protocol DiscoveryCallback <NSObject>
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
- (void) startDiscovery;
|
||||
- (void) stopDiscovery;
|
||||
- (void) stopDiscoveryBlocking;
|
||||
- (BOOL) addHostToDiscovery:(Host*)host;
|
||||
- (void) removeHostFromDiscovery:(Host*)host;
|
||||
- (void) discoverHost:(NSString*)hostAddress withCallback:(void (^)(Host*))callback;
|
||||
- (BOOL) addHostToDiscovery:(TemporaryHost*)host;
|
||||
- (void) removeHostFromDiscovery:(TemporaryHost*)host;
|
||||
- (void) discoverHost:(NSString*)hostAddress withCallback:(void (^)(TemporaryHost*, NSString*))callback;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// DiscoveryManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/1/15.
|
||||
// Copyright (c) 2015 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DiscoveryManager.h"
|
||||
@@ -12,10 +12,11 @@
|
||||
#import "Utils.h"
|
||||
#import "DataManager.h"
|
||||
#import "DiscoveryWorker.h"
|
||||
#import "ServerInfoResponse.h"
|
||||
#import "IdManager.h"
|
||||
|
||||
@implementation DiscoveryManager {
|
||||
NSMutableArray* _hostQueue;
|
||||
NSMutableArray* _discoveredHosts;
|
||||
id<DiscoveryCallback> _callback;
|
||||
MDNSManager* _mdnsMan;
|
||||
NSOperationQueue* _opQueue;
|
||||
@@ -26,61 +27,75 @@
|
||||
|
||||
- (id)initWithHosts:(NSArray *)hosts andCallback:(id<DiscoveryCallback>)callback {
|
||||
self = [super init];
|
||||
_hostQueue = [NSMutableArray arrayWithArray:hosts];
|
||||
|
||||
// Using addHostToDiscovery ensures no duplicates
|
||||
// will make it into the list from the database
|
||||
_callback = callback;
|
||||
shouldDiscover = NO;
|
||||
_hostQueue = [NSMutableArray array];
|
||||
for (TemporaryHost* host in hosts)
|
||||
{
|
||||
[self addHostToDiscovery:host];
|
||||
}
|
||||
[_callback updateAllHosts:_hostQueue];
|
||||
|
||||
_opQueue = [[NSOperationQueue alloc] init];
|
||||
_mdnsMan = [[MDNSManager alloc] initWithCallback:self];
|
||||
[CryptoManager generateKeyPairUsingSSl];
|
||||
_uniqueId = [CryptoManager getUniqueID];
|
||||
_uniqueId = [IdManager getUniqueId];
|
||||
_cert = [CryptoManager readCertFromFile];
|
||||
shouldDiscover = NO;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(Host *))callback {
|
||||
- (void) discoverHost:(NSString *)hostAddress withCallback:(void (^)(TemporaryHost *, NSString*))callback {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:hostAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
|
||||
ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest] fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
||||
|
||||
Host* host = nil;
|
||||
if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) {
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
host = [dataMan createHost];
|
||||
host.address = hostAddress;
|
||||
[HttpManager populateHostFromXML:serverInfoData host:host];
|
||||
TemporaryHost* host = nil;
|
||||
if ([serverInfoResponse isStatusOk]) {
|
||||
host = [[TemporaryHost alloc] init];
|
||||
host.activeAddress = host.address = hostAddress;
|
||||
host.online = YES;
|
||||
[serverInfoResponse populateHost:host];
|
||||
if (![self addHostToDiscovery:host]) {
|
||||
[dataMan removeHost:host];
|
||||
callback(nil, @"Host already added");
|
||||
} else {
|
||||
callback(host, nil);
|
||||
}
|
||||
} else {
|
||||
callback(nil, @"Could not connect to host");
|
||||
}
|
||||
callback(host);
|
||||
|
||||
}
|
||||
|
||||
- (void) startDiscovery {
|
||||
NSLog(@"Starting discovery");
|
||||
Log(LOG_I, @"Starting discovery");
|
||||
shouldDiscover = YES;
|
||||
[_mdnsMan searchForHosts];
|
||||
for (Host* host in _hostQueue) {
|
||||
for (TemporaryHost* host in _hostQueue) {
|
||||
[_opQueue addOperation:[self createWorkerForHost:host]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) stopDiscovery {
|
||||
NSLog(@"Stopping discovery");
|
||||
Log(LOG_I, @"Stopping discovery");
|
||||
shouldDiscover = NO;
|
||||
[_mdnsMan stopSearching];
|
||||
[_opQueue cancelAllOperations];
|
||||
}
|
||||
|
||||
- (void) stopDiscoveryBlocking {
|
||||
NSLog(@"Stopping discovery and waiting for workers to stop");
|
||||
Log(LOG_I, @"Stopping discovery and waiting for workers to stop");
|
||||
shouldDiscover = NO;
|
||||
[_mdnsMan stopSearching];
|
||||
[_opQueue cancelAllOperations];
|
||||
[_opQueue waitUntilAllOperationsAreFinished];
|
||||
NSLog(@"All discovery workers stopped");
|
||||
Log(LOG_I, @"All discovery workers stopped");
|
||||
}
|
||||
|
||||
- (BOOL) addHostToDiscovery:(Host *)host {
|
||||
if (![self isHostInDiscovery:host]) {
|
||||
- (BOOL) addHostToDiscovery:(TemporaryHost *)host {
|
||||
if (host.uuid.length > 0 && ![self isHostInDiscovery:host]) {
|
||||
[_hostQueue addObject:host];
|
||||
if (shouldDiscover) {
|
||||
[_opQueue addOperation:[self createWorkerForHost:host]];
|
||||
@@ -90,7 +105,7 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) removeHostFromDiscovery:(Host *)host {
|
||||
- (void) removeHostFromDiscovery:(TemporaryHost *)host {
|
||||
for (DiscoveryWorker* worker in [_opQueue operations]) {
|
||||
if ([worker getHost] == host) {
|
||||
[worker cancel];
|
||||
@@ -102,27 +117,25 @@
|
||||
// Override from MDNSCallback
|
||||
- (void)updateHosts:(NSArray *)hosts {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
// Discover the hosts before adding to eliminate duplicates
|
||||
for (Host* host in hosts) {
|
||||
NSLog(@"Found host through MDNS: %@:", host.name);
|
||||
for (TemporaryHost* host in hosts) {
|
||||
Log(LOG_I, @"Found host through MDNS: %@:", host.name);
|
||||
// Since this is on a background thread, we do not need to use the opQueue
|
||||
DiscoveryWorker* worker = (DiscoveryWorker*)[self createWorkerForHost:host];
|
||||
[worker discoverHost];
|
||||
if ([self addHostToDiscovery:host]) {
|
||||
NSLog(@"Adding host to discovery: %@", host.name);
|
||||
Log(LOG_I, @"Adding host to discovery: %@", host.name);
|
||||
[_callback updateAllHosts:_hostQueue];
|
||||
} else {
|
||||
NSLog(@"Not adding host to discovery: %@", host.name);
|
||||
[dataMan removeHost:host];
|
||||
Log(LOG_I, @"Not adding host to discovery: %@", host.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL) isHostInDiscovery:(Host*)host {
|
||||
- (BOOL) isHostInDiscovery:(TemporaryHost*)host {
|
||||
for (int i = 0; i < _hostQueue.count; i++) {
|
||||
Host* discoveredHost = [_hostQueue objectAtIndex:i];
|
||||
TemporaryHost* discoveredHost = [_hostQueue objectAtIndex:i];
|
||||
if (discoveredHost.uuid.length > 0 && [discoveredHost.uuid isEqualToString:host.uuid]) {
|
||||
return YES;
|
||||
}
|
||||
@@ -130,7 +143,7 @@
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSOperation*) createWorkerForHost:(Host*)host {
|
||||
- (NSOperation*) createWorkerForHost:(TemporaryHost*)host {
|
||||
DiscoveryWorker* worker = [[DiscoveryWorker alloc] initWithHost:host uniqueId:_uniqueId cert:_cert];
|
||||
return worker;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
//
|
||||
// DiscoveryWorker.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/2/15.
|
||||
// Copyright (c) 2015 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Host.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
@interface DiscoveryWorker : NSOperation
|
||||
|
||||
- (id) initWithHost:(Host*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert;
|
||||
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert;
|
||||
- (void) discoverHost;
|
||||
- (Host*) getHost;
|
||||
- (TemporaryHost*) getHost;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
//
|
||||
// DiscoveryWorker.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/2/15.
|
||||
// Copyright (c) 2015 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DiscoveryWorker.h"
|
||||
#import "Utils.h"
|
||||
#import "HttpManager.h"
|
||||
#import "ServerInfoResponse.h"
|
||||
#import "HttpRequest.h"
|
||||
#import "DataManager.h"
|
||||
|
||||
@implementation DiscoveryWorker {
|
||||
Host* _host;
|
||||
TemporaryHost* _host;
|
||||
NSString* _uniqueId;
|
||||
NSData* _cert;
|
||||
}
|
||||
|
||||
static const float POLL_RATE = 2.0f; // Poll every 2 seconds
|
||||
|
||||
- (id) initWithHost:(Host*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert {
|
||||
- (id) initWithHost:(TemporaryHost*)host uniqueId:(NSString*)uniqueId cert:(NSData*)cert {
|
||||
self = [super init];
|
||||
_host = host;
|
||||
_uniqueId = uniqueId;
|
||||
@@ -26,7 +29,7 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
|
||||
return self;
|
||||
}
|
||||
|
||||
- (Host*) getHost {
|
||||
- (TemporaryHost*) getHost {
|
||||
return _host;
|
||||
}
|
||||
|
||||
@@ -39,42 +42,113 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
|
||||
}
|
||||
}
|
||||
|
||||
- (void) discoverHost {
|
||||
BOOL receivedResponse = NO;
|
||||
if (!self.cancelled && _host.localAddress != nil) {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.localAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
|
||||
if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) {
|
||||
[HttpManager populateHostFromXML:serverInfoData host:_host];
|
||||
_host.address = _host.localAddress;
|
||||
receivedResponse = YES;
|
||||
}
|
||||
- (NSArray*) getHostAddressList {
|
||||
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:3];
|
||||
|
||||
if (_host.localAddress != nil) {
|
||||
[array addObject:_host.localAddress];
|
||||
}
|
||||
if (!self.cancelled && !receivedResponse && _host.externalAddress != nil) {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.externalAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
|
||||
if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) {
|
||||
[HttpManager populateHostFromXML:serverInfoData host:_host];
|
||||
_host.address = _host.externalAddress;
|
||||
receivedResponse = YES;
|
||||
if (_host.externalAddress != nil) {
|
||||
[array addObject:_host.externalAddress];
|
||||
}
|
||||
if (_host.address != nil) {
|
||||
[array addObject:_host.address];
|
||||
}
|
||||
|
||||
// Remove duplicate addresses from the list.
|
||||
// This is done using an array rather than a set
|
||||
// to preserve insertion order of addresses.
|
||||
for (int i = 0; i < [array count]; i++) {
|
||||
NSString *addr1 = [array objectAtIndex:i];
|
||||
|
||||
for (int j = 1; j < [array count]; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString *addr2 = [array objectAtIndex:j];
|
||||
|
||||
if ([addr1 isEqualToString:addr2]) {
|
||||
// Remove the last address
|
||||
[array removeObjectAtIndex:j];
|
||||
|
||||
// Begin searching again from the start
|
||||
i = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!self.cancelled && !receivedResponse && _host.address != nil) {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* serverInfoData = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
|
||||
if ([[HttpManager getStatusStringFromXML:serverInfoData] isEqualToString:@"OK"]) {
|
||||
[HttpManager populateHostFromXML:serverInfoData host:_host];
|
||||
receivedResponse = YES;
|
||||
return array;
|
||||
}
|
||||
|
||||
- (void) discoverHost {
|
||||
BOOL receivedResponse = NO;
|
||||
NSArray *addresses = [self getHostAddressList];
|
||||
|
||||
Log(LOG_D, @"%@ has %d unique addresses", _host.name, [addresses count]);
|
||||
|
||||
// Give the PC 3 tries to respond before declaring it offline
|
||||
for (int i = 0; i < 3; i++) {
|
||||
for (NSString *address in addresses) {
|
||||
if (self.cancelled) {
|
||||
// Get out without updating the status because
|
||||
// it might not have finished checking the various
|
||||
// addresses
|
||||
return;
|
||||
}
|
||||
|
||||
ServerInfoResponse* serverInfoResp = [self requestInfoAtAddress:address];
|
||||
receivedResponse = [self checkResponse:serverInfoResp];
|
||||
if (receivedResponse) {
|
||||
[serverInfoResp populateHost:_host];
|
||||
_host.activeAddress = address;
|
||||
|
||||
// Update the database using the response
|
||||
DataManager *dataManager = [[DataManager alloc] init];
|
||||
[dataManager updateHost:_host];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (receivedResponse) {
|
||||
Log(LOG_I, @"Received serverinfo response on try %d", i);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
// Wait for one second then retry
|
||||
[NSThread sleepForTimeInterval:1];
|
||||
}
|
||||
}
|
||||
|
||||
_host.online = receivedResponse;
|
||||
if (receivedResponse) {
|
||||
NSLog(@"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}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.uuid, _host.mac, _host.pairState, _host.online);
|
||||
} else {
|
||||
// If the host is not online, we do not know the pairstate
|
||||
_host.pairState = PairStateUnknown;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
- (ServerInfoResponse*) requestInfoAtAddress:(NSString*)address {
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:address
|
||||
uniqueId:_uniqueId
|
||||
deviceName:deviceName
|
||||
cert:_cert];
|
||||
ServerInfoResponse* response = [[ServerInfoResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:response
|
||||
withUrlRequest:[hMan newServerInfoRequest]
|
||||
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
||||
return response;
|
||||
}
|
||||
|
||||
- (BOOL) checkResponse:(ServerInfoResponse*)response {
|
||||
if ([response isStatusOk]) {
|
||||
// If the response is from a different host then do not update this host
|
||||
if ((_host.uuid == nil || [[response getStringTag:TAG_UNIQUE_ID] isEqualToString:_host.uuid])) {
|
||||
return YES;
|
||||
} else {
|
||||
Log(LOG_I, @"Received response from incorrect host: %@ expected: %@", [response getStringTag:TAG_UNIQUE_ID], _host.uuid);
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
//
|
||||
// HttpManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/16/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Host.h"
|
||||
#import "HttpResponse.h"
|
||||
#import "HttpRequest.h"
|
||||
|
||||
@interface HttpManager : NSObject <NSURLConnectionDelegate, NSURLConnectionDataDelegate>
|
||||
|
||||
+ (NSArray*) getAppListFromXML:(NSData*)xml;
|
||||
+ (void) populateHostFromXML:(NSData*)xml host:(Host*)host;
|
||||
+ (NSString*) getStringFromXML:(NSData*)xml tag:(NSString*)tag;
|
||||
+ (NSString*) getStatusStringFromXML:(NSData*)xml;
|
||||
@interface HttpManager : NSObject <NSURLSessionDelegate>
|
||||
|
||||
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert;
|
||||
- (NSURLRequest*) newPairRequest:(NSData*)salt;
|
||||
@@ -25,11 +21,13 @@
|
||||
- (NSURLRequest*) newPairChallenge;
|
||||
- (NSURLRequest*) newAppListRequest;
|
||||
- (NSURLRequest*) newServerInfoRequest;
|
||||
- (NSURLRequest*) newHttpServerInfoRequest;
|
||||
- (NSURLRequest*) newLaunchRequest:(NSString*)appId width:(int)width height:(int)height refreshRate:(int)refreshRate rikey:(NSString*)rikey rikeyid:(int)rikeyid;
|
||||
- (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId;
|
||||
- (NSURLRequest*) newQuitAppRequest;
|
||||
- (NSURLRequest*) newAppAssetRequestWithAppId:(NSString*)appId;
|
||||
- (NSData*) executeRequestSynchronously:(NSURLRequest*)request;
|
||||
- (void) executeRequestSynchronously:(HttpRequest*)request;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
//
|
||||
// HttpManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/16/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpManager.h"
|
||||
#import "HttpRequest.h"
|
||||
#import "CryptoManager.h"
|
||||
#import "App.h"
|
||||
#import "TemporaryApp.h"
|
||||
|
||||
#include <libxml2/libxml/xmlreader.h>
|
||||
#include <string.h>
|
||||
|
||||
@implementation HttpManager {
|
||||
NSString* _baseURL;
|
||||
NSURLSession* _urlSession;
|
||||
NSString* _baseHTTPURL;
|
||||
NSString* _baseHTTPSURL;
|
||||
NSString* _host;
|
||||
NSString* _uniqueId;
|
||||
NSString* _deviceName;
|
||||
@@ -22,210 +25,12 @@
|
||||
NSMutableData* _respData;
|
||||
NSData* _requestResp;
|
||||
dispatch_semaphore_t _requestLock;
|
||||
|
||||
BOOL _errorOccurred;
|
||||
}
|
||||
|
||||
static const NSString* PORT = @"47984";
|
||||
|
||||
+ (NSArray*) getAppListFromXML:(NSData*)xml {
|
||||
xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]);
|
||||
|
||||
if (docPtr == NULL) {
|
||||
NSLog(@"ERROR: An error occured trying to parse xml.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr rootNode = node = xmlDocGetRootElement(docPtr);
|
||||
|
||||
// Check root status_code
|
||||
if (![HttpManager verifyStatus: rootNode]) {
|
||||
NSLog(@"ERROR: Request returned with failure status");
|
||||
xmlFreeDoc(docPtr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Skip the root node
|
||||
node = node->children;
|
||||
|
||||
NSMutableArray* appList = [[NSMutableArray alloc] init];
|
||||
|
||||
while (node != NULL) {
|
||||
//NSLog(@"node: %s", node->name);
|
||||
if (!xmlStrcmp(node->name, (const xmlChar*)"App")) {
|
||||
xmlNodePtr appInfoNode = node->xmlChildrenNode;
|
||||
NSString* appName;
|
||||
NSString* appId;
|
||||
BOOL appIsRunning = NO;
|
||||
while (appInfoNode != NULL) {
|
||||
//NSLog(@"appInfoNode: %s", appInfoNode->name);
|
||||
if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"AppTitle")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
|
||||
appName = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"ID")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
|
||||
appId = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(appInfoNode->name, (const xmlChar*)"IsRunning")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, appInfoNode->xmlChildrenNode, 1);
|
||||
appIsRunning = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"];
|
||||
xmlFree(nodeVal);
|
||||
}
|
||||
appInfoNode = appInfoNode->next;
|
||||
}
|
||||
App* app = [[App alloc] init];
|
||||
app.appName = appName;
|
||||
app.appId = appId;
|
||||
app.isRunning = appIsRunning;
|
||||
[appList addObject:app];
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
xmlFreeDoc(docPtr);
|
||||
|
||||
return appList;
|
||||
}
|
||||
|
||||
+ (void) populateHostFromXML:(NSData*)xml host:(Host*)host {
|
||||
xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]);
|
||||
|
||||
if (docPtr == NULL) {
|
||||
NSLog(@"ERROR: An error occured trying to parse xml.");
|
||||
return;
|
||||
}
|
||||
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr rootNode = node = xmlDocGetRootElement(docPtr);
|
||||
|
||||
// Check root status_code
|
||||
if (![HttpManager verifyStatus: rootNode]) {
|
||||
NSLog(@"ERROR: Request returned with failure status");
|
||||
xmlFreeDoc(docPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the root node
|
||||
node = node->children;
|
||||
|
||||
while (node != NULL) {
|
||||
//NSLog(@"node: %s", node->name);
|
||||
if (!xmlStrcmp(node->name, (const xmlChar*)"hostname")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
host.name = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(node->name, (const xmlChar*)"ExternalIP")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
host.externalAddress = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(node->name, (const xmlChar*)"LocalIP")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
host.localAddress = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(node->name, (const xmlChar*)"uniqueid")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
host.uuid = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(node->name, (const xmlChar*)"mac")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
host.mac = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
} else if (!xmlStrcmp(node->name, (const xmlChar*)"PairStatus")) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
host.pairState = [[[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding] isEqualToString:@"1"] ? PairStatePaired : PairStateUnpaired;
|
||||
xmlFree(nodeVal);
|
||||
}
|
||||
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
xmlFreeDoc(docPtr);
|
||||
}
|
||||
|
||||
+ (NSString*) getStatusStringFromXML:(NSData*)xml {
|
||||
xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]);
|
||||
|
||||
if (docPtr == NULL) {
|
||||
NSLog(@"ERROR: An error occured trying to parse xml.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NSString* string;
|
||||
xmlNodePtr rootNode = xmlDocGetRootElement(docPtr);
|
||||
if (rootNode == NULL) {
|
||||
NSLog(@"ERROR: No root XML element.");
|
||||
xmlFreeDoc(docPtr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string = [HttpManager getStatusMessage: rootNode];
|
||||
|
||||
xmlFreeDoc(docPtr);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
+ (NSString*) getStringFromXML:(NSData*)xml tag:(NSString*)tag {
|
||||
xmlDocPtr docPtr = xmlParseMemory([xml bytes], (int)[xml length]);
|
||||
|
||||
if (docPtr == NULL) {
|
||||
NSLog(@"ERROR: An error occured trying to parse xml.");
|
||||
return NULL;
|
||||
}
|
||||
NSString* value;
|
||||
xmlNodePtr node;
|
||||
xmlNodePtr rootNode = node = xmlDocGetRootElement(docPtr);
|
||||
|
||||
// Check root status_code
|
||||
if (![HttpManager verifyStatus: rootNode]) {
|
||||
NSLog(@"ERROR: Request returned with failure status");
|
||||
xmlFreeDoc(docPtr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Skip the root node
|
||||
node = node->children;
|
||||
|
||||
while (node != NULL) {
|
||||
//NSLog(@"node: %s", node->name);
|
||||
if (!xmlStrcmp(node->name, (const xmlChar*)[tag UTF8String])) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
value = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
xmlFree(nodeVal);
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
//NSLog(@"xmlValue: %@", value);
|
||||
|
||||
xmlFreeDoc(docPtr);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
+ (NSString*) getStatusMessage:(xmlNodePtr)docRoot {
|
||||
xmlChar* statusMsgXml = xmlGetProp(docRoot, (const xmlChar*)"status_message");
|
||||
NSString* statusMsg;
|
||||
if (statusMsgXml != NULL) {
|
||||
statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml];
|
||||
xmlFree(statusMsgXml);
|
||||
}
|
||||
else {
|
||||
statusMsg = @"Server Error";
|
||||
}
|
||||
|
||||
return statusMsg;
|
||||
}
|
||||
|
||||
+ (bool) verifyStatus:(xmlNodePtr)docRoot {
|
||||
xmlChar* statusStr = xmlGetProp(docRoot, (const xmlChar*)"status_code");
|
||||
if (statusStr != NULL) {
|
||||
int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue];
|
||||
xmlFree(statusStr);
|
||||
return status == 200;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
static const NSString* HTTP_PORT = @"47989";
|
||||
static const NSString* HTTPS_PORT = @"47984";
|
||||
|
||||
+ (NSData*) fixXmlVersion:(NSData*) xmlData {
|
||||
NSString* dataString = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding];
|
||||
@@ -240,34 +45,63 @@ static const NSString* PORT = @"47984";
|
||||
_uniqueId = uniqueId;
|
||||
_deviceName = deviceName;
|
||||
_cert = cert;
|
||||
_baseURL = [[NSString stringWithFormat:@"https://%@:%@", host, PORT]
|
||||
stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
_baseHTTPURL = [NSString stringWithFormat:@"http://%@:%@", host, HTTP_PORT];
|
||||
_baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%@", host, HTTPS_PORT];
|
||||
_requestLock = dispatch_semaphore_create(0);
|
||||
_respData = [[NSMutableData alloc] init];
|
||||
NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
_urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSData*) executeRequestSynchronously:(NSURLRequest*)request {
|
||||
NSLog(@"Making Request: %@", request);
|
||||
- (void) executeRequestSynchronously:(HttpRequest*)request {
|
||||
Log(LOG_D, @"Making Request: %@", request);
|
||||
[_respData setLength:0];
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[NSURLConnection connectionWithRequest:request delegate:self];
|
||||
});
|
||||
dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER);
|
||||
return _requestResp;
|
||||
}
|
||||
[[_urlSession dataTaskWithRequest:request.request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
|
||||
|
||||
if (error != NULL) {
|
||||
Log(LOG_D, @"Connection error: %@", error);
|
||||
_errorOccurred = true;
|
||||
}
|
||||
else {
|
||||
Log(LOG_D, @"Received response: %@", response);
|
||||
|
||||
- (void) executeRequest:(NSURLRequest*)request {
|
||||
[NSURLConnection connectionWithRequest:request delegate:self];
|
||||
if (data != NULL) {
|
||||
Log(LOG_D, @"\n\nReceived data: %@\n\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
|
||||
[_respData appendData:data];
|
||||
if ([[NSString alloc] initWithData:_respData encoding:NSUTF8StringEncoding] != nil) {
|
||||
_requestResp = [HttpManager fixXmlVersion:_respData];
|
||||
} else {
|
||||
_requestResp = _respData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_semaphore_signal(_requestLock);
|
||||
}] resume];
|
||||
dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER);
|
||||
|
||||
if (!_errorOccurred && request.response) {
|
||||
[request.response populateWithData:_requestResp];
|
||||
|
||||
// If the fallback error code was detected, issue the fallback request
|
||||
if (request.response.statusCode == request.fallbackError && request.fallbackRequest != NULL) {
|
||||
Log(LOG_D, @"Request failed with fallback error code: %d", request.fallbackError);
|
||||
request.request = request.fallbackRequest;
|
||||
request.fallbackError = 0;
|
||||
request.fallbackRequest = NULL;
|
||||
[self executeRequestSynchronously:request];
|
||||
}
|
||||
}
|
||||
_errorOccurred = false;
|
||||
}
|
||||
|
||||
- (NSURLRequest*) createRequestFromString:(NSString*) urlString enableTimeout:(BOOL)normalTimeout {
|
||||
NSString* escapedUrl = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
NSURL* url = [[NSURL alloc] initWithString:escapedUrl];
|
||||
NSURL* url = [[NSURL alloc] initWithString:urlString];
|
||||
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
|
||||
if (normalTimeout) {
|
||||
// Timeout the request after 5 seconds
|
||||
[request setTimeoutInterval:5];
|
||||
// Timeout the request after 7 seconds
|
||||
[request setTimeoutInterval:7];
|
||||
}
|
||||
else {
|
||||
// Timeout the request after 60 seconds
|
||||
@@ -278,67 +112,72 @@ static const NSString* PORT = @"47984";
|
||||
|
||||
- (NSURLRequest*) newPairRequest:(NSData*)salt {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=getservercert&salt=%@&clientcert=%@",
|
||||
_baseURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]];
|
||||
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]];
|
||||
// This call blocks while waiting for the user to input the PIN on the PC
|
||||
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newUnpairRequest {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseURL, _uniqueId];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newChallengeRequest:(NSData*)challenge {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientchallenge=%@",
|
||||
_baseURL, _uniqueId, _deviceName, [self bytesToHex:challenge]];
|
||||
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challenge]];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newChallengeRespRequest:(NSData*)challengeResp {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&serverchallengeresp=%@",
|
||||
_baseURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]];
|
||||
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newClientSecretRespRequest:(NSString*)clientPairSecret {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseURL, _uniqueId, _deviceName, clientPairSecret];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseHTTPSURL, _uniqueId, _deviceName, clientPairSecret];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newPairChallenge {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseURL, _uniqueId, _deviceName];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseHTTPSURL, _uniqueId, _deviceName];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest *)newAppListRequest {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseURL, _uniqueId];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest *)newServerInfoRequest {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseURL, _uniqueId];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest *)newHttpServerInfoRequest {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo", _baseHTTPURL];
|
||||
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newLaunchRequest:(NSString*)appId width:(int)width height:(int)height refreshRate:(int)refreshRate rikey:(NSString*)rikey rikeyid:(int)rikeyid {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/launch?uniqueid=%@&appid=%@&mode=%dx%dx%d&additionalStates=1&sops=1&rikey=%@&rikeyid=%d", _baseURL, _uniqueId, appId, width, height, refreshRate, rikey, rikeyid];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/launch?uniqueid=%@&appid=%@&mode=%dx%dx%d&additionalStates=1&sops=1&rikey=%@&rikeyid=%d", _baseHTTPSURL, _uniqueId, appId, width, height, refreshRate, rikey, rikeyid];
|
||||
// This blocks while the app is launching
|
||||
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d", _baseURL, _uniqueId, riKey, riKeyId];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d", _baseHTTPSURL, _uniqueId, riKey, riKeyId];
|
||||
// This blocks while the app is resuming
|
||||
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newQuitAppRequest {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/cancel?uniqueid=%@", _baseURL, _uniqueId];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/cancel?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
||||
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
||||
}
|
||||
|
||||
- (NSURLRequest*) newAppAssetRequestWithAppId:(NSString *)appId {
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/appasset?uniqueid=%@&appid=%@&AssetType=2&AssetIdx=0", _baseURL, _uniqueId, appId];
|
||||
NSString* urlString = [NSString stringWithFormat:@"%@/appasset?uniqueid=%@&appid=%@&AssetType=2&AssetIdx=0", _baseHTTPSURL, _uniqueId, appId];
|
||||
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
||||
}
|
||||
|
||||
@@ -351,33 +190,6 @@ static const NSString* PORT = @"47984";
|
||||
return hex;
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
NSLog(@"Received response: %@", response);
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
NSLog(@"\n\nReceived data: %@\n\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
|
||||
[_respData appendData:data];
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
if ([[NSString alloc] initWithData:_respData encoding:NSUTF8StringEncoding] != nil) {
|
||||
_requestResp = [HttpManager fixXmlVersion:_respData];
|
||||
} else {
|
||||
_requestResp = _respData;
|
||||
}
|
||||
dispatch_semaphore_signal(_requestLock);
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
|
||||
SecIdentityRef identity = [self getClientCertificate];
|
||||
NSArray *certArray = [self getCertificate:identity];
|
||||
|
||||
NSURLCredential *newCredential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent];
|
||||
|
||||
[challenge.sender useCredential:newCredential forAuthenticationChallenge:challenge];
|
||||
}
|
||||
|
||||
// Returns an array containing the certificate
|
||||
- (NSArray*)getCertificate:(SecIdentityRef) identity {
|
||||
SecCertificateRef certificate = nil;
|
||||
@@ -400,11 +212,11 @@ static const NSString* PORT = @"47984";
|
||||
OSStatus securityError = SecPKCS12Import(p12Data, options, &items);
|
||||
|
||||
if (securityError == errSecSuccess) {
|
||||
//NSLog(@"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
|
||||
//Log(LOG_D, @"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
|
||||
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
|
||||
identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
|
||||
} else {
|
||||
NSLog(@"Error opening Certificate.");
|
||||
Log(LOG_E, @"Error opening Certificate.");
|
||||
}
|
||||
|
||||
CFRelease(options);
|
||||
@@ -413,9 +225,25 @@ static const NSString* PORT = @"47984";
|
||||
return identityApp;
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
NSLog(@"connection error: %@", error);
|
||||
dispatch_semaphore_signal(_requestLock);
|
||||
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * __nullable))completionHandler {
|
||||
// Allow untrusted server certificates
|
||||
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
|
||||
{
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential,
|
||||
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]);
|
||||
}
|
||||
// Respond to client certificate challenge with our certificate
|
||||
else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
|
||||
{
|
||||
SecIdentityRef identity = [self getClientCertificate];
|
||||
NSArray* certArray = [self getCertificate:identity];
|
||||
NSURLCredential* newCredential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent];
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential);
|
||||
}
|
||||
else
|
||||
{
|
||||
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// HttpRequest.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "HttpResponse.h"
|
||||
|
||||
@interface HttpRequest : NSObject
|
||||
|
||||
@property (nonatomic) id<Response> response;
|
||||
@property (nonatomic) NSURLRequest* request;
|
||||
@property (nonatomic) int fallbackError;
|
||||
@property (nonatomic) NSURLRequest* fallbackRequest;
|
||||
|
||||
+ (instancetype) requestForResponse:(id<Response>)response withUrlRequest:(NSURLRequest*)req fallbackError:(int)error fallbackRequest:(NSURLRequest*) fallbackReq;
|
||||
+ (instancetype) requestForResponse:(id<Response>)response withUrlRequest:(NSURLRequest*)req;
|
||||
+ (instancetype) requestWithUrlRequest:(NSURLRequest*)req;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// HttpRequest.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpRequest.h"
|
||||
#import "HttpResponse.h"
|
||||
#import "HttpManager.h"
|
||||
|
||||
@implementation HttpRequest
|
||||
|
||||
+ (HttpRequest*) requestForResponse:(id<Response>)response withUrlRequest:(NSURLRequest*)req {
|
||||
HttpRequest* request = [[HttpRequest alloc] init];
|
||||
request.request = req;
|
||||
request.response = response;
|
||||
return request;
|
||||
}
|
||||
|
||||
+ (HttpRequest*) requestWithUrlRequest:(NSURLRequest*)req {
|
||||
HttpRequest* request = [[HttpRequest alloc] init];
|
||||
request.request = req;
|
||||
return request;
|
||||
}
|
||||
|
||||
+ (HttpRequest*) requestForResponse:(id<Response>)response withUrlRequest:(NSURLRequest*)req fallbackError:(int)error fallbackRequest:(NSURLRequest*) fallbackReq {
|
||||
HttpRequest* request = [[HttpRequest alloc] init];
|
||||
request.request = req;
|
||||
request.response = response;
|
||||
request.fallbackError = error;
|
||||
request.fallbackRequest = fallbackReq;
|
||||
return request;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// HttpResponse.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/30/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
static NSString* TAG_STATUS_CODE = @"status_code";
|
||||
static NSString* TAG_STATUS_MESSAGE = @"status_message";
|
||||
|
||||
@protocol Response <NSObject>
|
||||
|
||||
- (void) populateWithData:(NSData*)data;
|
||||
|
||||
@property (nonatomic) NSInteger statusCode;
|
||||
@property (nonatomic) NSString* statusMessage;
|
||||
@property (nonatomic) NSData* data;
|
||||
|
||||
@end
|
||||
|
||||
@interface HttpResponse : NSObject <Response>
|
||||
|
||||
- (void) populateWithData:(NSData*)data;
|
||||
- (void) parseData;
|
||||
- (NSString*) getStringTag:(NSString*)tag;
|
||||
- (BOOL) getIntTag:(NSString *)tag value:(NSInteger*)value;
|
||||
- (BOOL) isStatusOk;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// HttpResponse.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/30/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpResponse.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import <libxml2/libxml/xmlreader.h>
|
||||
|
||||
@implementation HttpResponse {
|
||||
NSMutableDictionary* _elements;
|
||||
}
|
||||
@synthesize data, statusCode, statusMessage;
|
||||
|
||||
- (void) populateWithData:(NSData*)xml {
|
||||
self.data = xml;
|
||||
[self parseData];
|
||||
}
|
||||
|
||||
- (NSString*) getStringTag:(NSString*)tag {
|
||||
return [_elements objectForKey:tag];
|
||||
}
|
||||
|
||||
- (BOOL) getIntTag:(NSString *)tag value:(NSInteger*)value {
|
||||
NSString* stringVal = [self getStringTag:tag];
|
||||
if (stringVal != nil) {
|
||||
*value = [stringVal integerValue];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) isStatusOk {
|
||||
return self.statusCode == 200;
|
||||
}
|
||||
|
||||
- (void) parseData {
|
||||
_elements = [[NSMutableDictionary alloc] init];
|
||||
xmlDocPtr docPtr = xmlParseMemory([self.data bytes], (int)[self.data length]);
|
||||
if (docPtr == NULL) {
|
||||
Log(LOG_W, @"An error occured trying to parse xml.");
|
||||
return;
|
||||
}
|
||||
|
||||
xmlNodePtr node = xmlDocGetRootElement(docPtr);
|
||||
if (node == NULL) {
|
||||
Log(LOG_W, @"No root XML element.");
|
||||
xmlFreeDoc(docPtr);
|
||||
return;
|
||||
}
|
||||
|
||||
xmlChar* statusStr = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_CODE UTF8String]);
|
||||
if (statusStr != NULL) {
|
||||
int status = [[NSString stringWithUTF8String:(const char*)statusStr] intValue];
|
||||
xmlFree(statusStr);
|
||||
self.statusCode = status;
|
||||
}
|
||||
|
||||
xmlChar* statusMsgXml = xmlGetProp(node, (const xmlChar*)[TAG_STATUS_MESSAGE UTF8String]);
|
||||
NSString* statusMsg;
|
||||
if (statusMsgXml != NULL) {
|
||||
statusMsg = [NSString stringWithUTF8String:(const char*)statusMsgXml];
|
||||
xmlFree(statusMsgXml);
|
||||
}
|
||||
else {
|
||||
statusMsg = @"Server Error";
|
||||
}
|
||||
self.statusMessage = statusMsg;
|
||||
|
||||
node = node->children;
|
||||
|
||||
while (node != NULL) {
|
||||
xmlChar* nodeVal = xmlNodeListGetString(docPtr, node->xmlChildrenNode, 1);
|
||||
|
||||
NSString* value;
|
||||
if (nodeVal == NULL) {
|
||||
value = @"";
|
||||
} else {
|
||||
value = [[NSString alloc] initWithCString:(const char*)nodeVal encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
NSString* key = [[NSString alloc] initWithCString:(const char*)node->name encoding:NSUTF8StringEncoding];
|
||||
[_elements setObject:value forKey:key];
|
||||
xmlFree(nodeVal);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
xmlFreeDoc(docPtr);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// MDNSManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/14/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
//
|
||||
// MDNSManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/14/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MDNSManager.h"
|
||||
#import "Host.h"
|
||||
#import "DataManager.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
@implementation MDNSManager {
|
||||
NSNetServiceBrowser* mDNSBrowser;
|
||||
@@ -34,24 +33,23 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
|
||||
}
|
||||
|
||||
- (void) searchForHosts {
|
||||
NSLog(@"Starting mDNS discovery");
|
||||
Log(LOG_I, @"Starting mDNS discovery");
|
||||
scanActive = TRUE;
|
||||
[mDNSBrowser searchForServicesOfType:NV_SERVICE_TYPE inDomain:@""];
|
||||
}
|
||||
|
||||
- (void) stopSearching {
|
||||
NSLog(@"Stopping mDNS discovery");
|
||||
Log(LOG_I, @"Stopping mDNS discovery");
|
||||
scanActive = FALSE;
|
||||
[mDNSBrowser stop];
|
||||
}
|
||||
|
||||
- (NSArray*) getFoundHosts {
|
||||
NSMutableArray* hosts = [[NSMutableArray alloc] init];
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
for (NSNetService* service in services) {
|
||||
if (service.hostName != nil) {
|
||||
Host* host = [dataMan createHost];
|
||||
host.address = service.hostName;
|
||||
TemporaryHost* host = [[TemporaryHost alloc] init];
|
||||
host.activeAddress = host.address = service.hostName;
|
||||
host.name = host.address;
|
||||
[hosts addObject:host];
|
||||
}
|
||||
@@ -60,12 +58,12 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
|
||||
}
|
||||
|
||||
- (void)netServiceDidResolveAddress:(NSNetService *)service {
|
||||
NSLog(@"Resolved address: %@ -> %@", service, service.hostName);
|
||||
Log(LOG_I, @"Resolved address: %@ -> %@", service, service.hostName);
|
||||
[self.callback updateHosts:[self getFoundHosts]];
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict {
|
||||
NSLog(@"Did not resolve address for: %@\n%@", sender, [errorDict description]);
|
||||
Log(LOG_W, @"Did not resolve address for: %@\n%@", sender, [errorDict description]);
|
||||
|
||||
// Schedule a retry in 2 seconds
|
||||
[NSTimer scheduledTimerWithTimeInterval:2.0
|
||||
@@ -76,7 +74,7 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
|
||||
NSLog(@"Found service: %@", aNetService);
|
||||
Log(LOG_I, @"Found service: %@", aNetService);
|
||||
[aNetService setDelegate:self];
|
||||
[aNetService resolveWithTimeout:5];
|
||||
|
||||
@@ -85,13 +83,12 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing {
|
||||
NSLog(@"Removing service: %@", aNetService);
|
||||
Log(LOG_I, @"Removing service: %@", aNetService);
|
||||
[services removeObject:aNetService];
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didNotSearch:(NSDictionary *)errorDict {
|
||||
NSLog(@"Did not perform search");
|
||||
NSLog(@"%@", [errorDict description]);
|
||||
Log(LOG_W, @"Did not perform search: \n%@", [errorDict description]);
|
||||
|
||||
// Schedule a retry in 2 seconds
|
||||
[NSTimer scheduledTimerWithTimeInterval:2.0
|
||||
@@ -107,7 +104,7 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"Retrying mDNS search");
|
||||
Log(LOG_I, @"Retrying mDNS search");
|
||||
[mDNSBrowser stop];
|
||||
[mDNSBrowser searchForServicesOfType:NV_SERVICE_TYPE inDomain:@""];
|
||||
}
|
||||
@@ -118,7 +115,7 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"Retrying mDNS resolution");
|
||||
Log(LOG_I, @"Retrying mDNS resolution");
|
||||
for (NSNetService* service in services) {
|
||||
if (service.hostName == nil) {
|
||||
[service setDelegate:self];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// PairManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/19/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
//
|
||||
// PairManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/19/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PairManager.h"
|
||||
#import "CryptoManager.h"
|
||||
#import "Utils.h"
|
||||
#import "HttpResponse.h"
|
||||
#import "HttpRequest.h"
|
||||
#import "ServerInfoResponse.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
|
||||
@@ -27,37 +30,43 @@
|
||||
}
|
||||
|
||||
- (void) main {
|
||||
NSData* serverInfo = [_httpManager executeRequestSynchronously:[_httpManager newServerInfoRequest]];
|
||||
if (serverInfo == NULL) {
|
||||
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[_httpManager newServerInfoRequest]
|
||||
fallbackError:401 fallbackRequest:[_httpManager newHttpServerInfoRequest]]];
|
||||
if (serverInfoResp == nil) {
|
||||
[_callback pairFailed:@"Unable to connect to PC"];
|
||||
return;
|
||||
}
|
||||
if (![[HttpManager getStringFromXML:serverInfo tag:@"currentgame"] isEqual:@"0"]) {
|
||||
[_callback pairFailed:@"You must stop streaming before attempting to pair."];
|
||||
}
|
||||
else if (![[HttpManager getStringFromXML:serverInfo tag:@"PairStatus"] isEqual:@"1"]) {
|
||||
[self initiatePair];
|
||||
} else {
|
||||
[_callback alreadyPaired];
|
||||
if ([serverInfoResp isStatusOk]) {
|
||||
if (![[serverInfoResp getStringTag:@"state"] hasSuffix:@"_SERVER_AVAILABLE"]) {
|
||||
[_callback pairFailed:@"You must stop streaming before attempting to pair."];
|
||||
} else if (![[serverInfoResp getStringTag:@"PairStatus"] isEqual:@"1"]) {
|
||||
[self initiatePair];
|
||||
} else {
|
||||
[_callback alreadyPaired];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) initiatePair {
|
||||
NSString* PIN = [self generatePIN];
|
||||
NSData* salt = [self saltPIN:PIN];
|
||||
NSLog(@"PIN: %@, saltedPIN: %@", PIN, salt);
|
||||
Log(LOG_I, @"PIN: %@, saltedPIN: %@", PIN, salt);
|
||||
[_callback showPIN:PIN];
|
||||
|
||||
NSData* pairResp = [_httpManager executeRequestSynchronously:[_httpManager newPairRequest:salt]];
|
||||
NSString* pairedString;
|
||||
pairedString = [HttpManager getStringFromXML:pairResp tag:@"paired"];
|
||||
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
HttpResponse* pairResp = [[HttpResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:pairResp withUrlRequest:[_httpManager newPairRequest:salt]]];
|
||||
if (![self verifyResponseStatus:pairResp]) {
|
||||
return;
|
||||
}
|
||||
NSInteger pairedStatus;
|
||||
if (![pairResp getIntTag:@"paired" value:&pairedStatus] || !pairedStatus) {
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Pairing was declined by the target."];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString* plainCert = [HttpManager getStringFromXML:pairResp tag:@"plaincert"];
|
||||
NSString* plainCert = [pairResp getStringTag:@"plaincert"];
|
||||
|
||||
CryptoManager* cryptoMan = [[CryptoManager alloc] init];
|
||||
NSData* aesKey = [cryptoMan createAESKeyFromSalt:salt];
|
||||
@@ -65,15 +74,18 @@
|
||||
NSData* randomChallenge = [Utils randomBytes:16];
|
||||
NSData* encryptedChallenge = [cryptoMan aesEncrypt:randomChallenge withKey:aesKey];
|
||||
|
||||
NSData* challengeResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRequest:encryptedChallenge]];
|
||||
pairedString = [HttpManager getStringFromXML:challengeResp tag:@"paired"];
|
||||
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
HttpResponse* challengeResp = [[HttpResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:challengeResp withUrlRequest:[_httpManager newChallengeRequest:encryptedChallenge]]];
|
||||
if (![self verifyResponseStatus:challengeResp]) {
|
||||
return;
|
||||
}
|
||||
if (![challengeResp getIntTag:@"paired" value:&pairedStatus] || pairedStatus != 1) {
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Pairing stage #2 failed"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* encServerChallengeResp = [Utils hexToBytes:[HttpManager getStringFromXML:challengeResp tag:@"challengeresponse"]];
|
||||
NSData* encServerChallengeResp = [Utils hexToBytes:[challengeResp getStringTag:@"challengeresponse"]];
|
||||
NSData* decServerChallengeResp = [cryptoMan aesDecrypt:encServerChallengeResp withKey:aesKey];
|
||||
|
||||
NSData* serverResponse = [decServerChallengeResp subdataWithRange:NSMakeRange(0, 20)];
|
||||
@@ -83,50 +95,73 @@
|
||||
NSData* challengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:serverChallenge with:[CryptoManager getSignatureFromCert:_cert]] with:clientSecret]];
|
||||
NSData* challengeRespEncrypted = [cryptoMan aesEncrypt:challengeRespHash withKey:aesKey];
|
||||
|
||||
NSData* secretResp = [_httpManager executeRequestSynchronously:[_httpManager newChallengeRespRequest:challengeRespEncrypted]];
|
||||
pairedString = [HttpManager getStringFromXML:secretResp tag:@"paired"];
|
||||
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
HttpResponse* secretResp = [[HttpResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:secretResp withUrlRequest:[_httpManager newChallengeRespRequest:challengeRespEncrypted]]];
|
||||
if (![self verifyResponseStatus:secretResp]) {
|
||||
return;
|
||||
}
|
||||
if (![secretResp getIntTag:@"paired" value:&pairedStatus] || pairedStatus != 1) {
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Pairing stage #3 failed"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* serverSecretResp = [Utils hexToBytes:[HttpManager getStringFromXML:secretResp tag:@"pairingsecret"]];
|
||||
NSData* serverSecretResp = [Utils hexToBytes:[secretResp getStringTag:@"pairingsecret"]];
|
||||
NSData* serverSecret = [serverSecretResp subdataWithRange:NSMakeRange(0, 16)];
|
||||
NSData* serverSignature = [serverSecretResp subdataWithRange:NSMakeRange(16, 256)];
|
||||
|
||||
if (![cryptoMan verifySignature:serverSecret withSignature:serverSignature andCert:[Utils hexToBytes:plainCert]]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Server certificate invalid"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* serverChallengeRespHash = [cryptoMan SHA1HashData:[self concatData:[self concatData:randomChallenge with:[CryptoManager getSignatureFromCert:[Utils hexToBytes:plainCert]]] with:serverSecret]];
|
||||
if (![serverChallengeRespHash isEqual:serverResponse]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Incorrect PIN"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* clientPairingSecret = [self concatData:clientSecret with:[cryptoMan signData:clientSecret withKey:[CryptoManager readKeyFromFile]]];
|
||||
NSData* clientSecretResp = [_httpManager executeRequestSynchronously:[_httpManager newClientSecretRespRequest:[Utils bytesToHex:clientPairingSecret]]];
|
||||
pairedString = [HttpManager getStringFromXML:clientSecretResp tag:@"paired"];
|
||||
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
HttpResponse* clientSecretResp = [[HttpResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:clientSecretResp withUrlRequest:[_httpManager newClientSecretRespRequest:[Utils bytesToHex:clientPairingSecret]]]];
|
||||
if (![self verifyResponseStatus:clientSecretResp]) {
|
||||
return;
|
||||
}
|
||||
if (![clientSecretResp getIntTag:@"paired" value:&pairedStatus] || pairedStatus != 1) {
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Pairing stage #4 failed"];
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* clientPairChallenge = [_httpManager executeRequestSynchronously:[_httpManager newPairChallenge]];
|
||||
pairedString = [HttpManager getStringFromXML:clientPairChallenge tag:@"paired"];
|
||||
if (pairedString == NULL || ![pairedString isEqualToString:@"1"]) {
|
||||
[_httpManager executeRequestSynchronously:[_httpManager newUnpairRequest]];
|
||||
HttpResponse* clientPairChallengeResp = [[HttpResponse alloc] init];
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestForResponse:clientPairChallengeResp withUrlRequest:[_httpManager newPairChallenge]]];
|
||||
if (![self verifyResponseStatus:clientPairChallengeResp]) {
|
||||
return;
|
||||
}
|
||||
if (![clientPairChallengeResp getIntTag:@"paired" value:&pairedStatus] || pairedStatus != 1) {
|
||||
[_httpManager executeRequestSynchronously:[HttpRequest requestWithUrlRequest:[_httpManager newUnpairRequest]]];
|
||||
[_callback pairFailed:@"Pairing stage #5 failed"];
|
||||
return;
|
||||
}
|
||||
[_callback pairSuccessful];
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData*) concatData:(NSData*)data with:(NSData*)moreData {
|
||||
NSMutableData* concatData = [[NSMutableData alloc] initWithData:data];
|
||||
[concatData appendData:moreData];
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// ServerInfoResponse.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HttpResponse.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
#define TAG_HOSTNAME @"hostname"
|
||||
#define TAG_EXTERNAL_IP @"ExternalIP"
|
||||
#define TAG_LOCAL_IP @"LocalIP"
|
||||
#define TAG_UNIQUE_ID @"uniqueid"
|
||||
#define TAG_MAC_ADDRESS @"mac"
|
||||
#define TAG_PAIR_STATUS @"PairStatus"
|
||||
|
||||
@interface ServerInfoResponse : HttpResponse <Response>
|
||||
|
||||
- (void) populateWithData:(NSData *)data;
|
||||
- (void) populateHost:(TemporaryHost*)host;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// ServerInfoResponse.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/1/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "ServerInfoResponse.h"
|
||||
#import <libxml2/libxml/xmlreader.h>
|
||||
|
||||
@implementation ServerInfoResponse
|
||||
@synthesize data, statusCode, statusMessage;
|
||||
|
||||
- (void) populateWithData:(NSData *)xml {
|
||||
self.data = xml;
|
||||
[super parseData];
|
||||
}
|
||||
|
||||
- (void) populateHost:(TemporaryHost*)host {
|
||||
host.name = [[self getStringTag:TAG_HOSTNAME] trim];
|
||||
host.externalAddress = [[self getStringTag:TAG_EXTERNAL_IP] 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];
|
||||
|
||||
NSInteger pairStatus;
|
||||
if ([self getIntTag:TAG_PAIR_STATUS value:&pairStatus]) {
|
||||
host.pairState = pairStatus ? PairStatePaired : PairStateUnpaired;
|
||||
} else {
|
||||
host.pairState = PairStateUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,16 +1,16 @@
|
||||
//
|
||||
// WakeOnLanManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/2/15.
|
||||
// Copyright (c) 2015 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "Host.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
@interface WakeOnLanManager : NSObject
|
||||
|
||||
+ (void) wakeHost:(Host*)host;
|
||||
+ (void) wakeHost:(TemporaryHost*)host;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// WakeOnLanManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/2/15.
|
||||
// Copyright (c) 2015 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WakeOnLanManager.h"
|
||||
@@ -18,16 +18,16 @@
|
||||
static const int numPorts = 5;
|
||||
static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
|
||||
|
||||
+ (void) wakeHost:(Host*)host {
|
||||
+ (void) wakeHost:(TemporaryHost*)host {
|
||||
NSData* wolPayload = [WakeOnLanManager createPayload:host];
|
||||
CFDataRef dataPayload = CFDataCreate(kCFAllocatorDefault, [wolPayload bytes], [wolPayload length]);
|
||||
CFSocketRef wolSocket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_DGRAM, IPPROTO_UDP, 0, NULL, NULL);
|
||||
if (!wolSocket) {
|
||||
CFRelease(dataPayload);
|
||||
NSLog(@"Failed to create WOL socket");
|
||||
Log(LOG_E, @"Failed to create WOL socket");
|
||||
return;
|
||||
}
|
||||
NSLog(@"WOL socket created");
|
||||
Log(LOG_I, @"WOL socket created");
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
@@ -48,7 +48,7 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
|
||||
// try all ports
|
||||
for (int j = 0; j < numPorts; j++) {
|
||||
addr.sin_port = htons(ports[j]);
|
||||
NSLog(@"Sending WOL packet");
|
||||
Log(LOG_I, @"Sending WOL packet");
|
||||
CFSocketSendData(wolSocket, dataAddress, dataPayload, 0);
|
||||
}
|
||||
CFRelease(dataAddress);
|
||||
@@ -56,7 +56,7 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
|
||||
CFRelease(dataPayload);
|
||||
}
|
||||
|
||||
+ (NSData*) createPayload:(Host*)host {
|
||||
+ (NSData*) createPayload:(TemporaryHost*)host {
|
||||
NSMutableData* payload = [[NSMutableData alloc] initWithCapacity:102];
|
||||
|
||||
// 6 bytes of FF
|
||||
@@ -76,7 +76,7 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
|
||||
|
||||
+ (NSData*) macStringToBytes:(NSString*)mac {
|
||||
NSString* macString = [mac stringByReplacingOccurrencesOfString:@":" withString:@""];
|
||||
NSLog(@"MAC: %@", macString);
|
||||
Log(LOG_D, @"MAC: %@", macString);
|
||||
return [Utils hexToBytes:macString];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Connection.h
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/19/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
@interface Connection : NSOperation <NSStreamDelegate>
|
||||
|
||||
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks;
|
||||
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks serverMajorVersion:(int)serverMajorVersion;
|
||||
-(void) terminate;
|
||||
-(void) terminateInternal;
|
||||
-(void) main;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Connection.m
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/19/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Connection.h"
|
||||
@@ -15,11 +15,12 @@
|
||||
#include "opus.h"
|
||||
|
||||
@implementation Connection {
|
||||
IP_ADDRESS _host;
|
||||
const char* _host;
|
||||
STREAM_CONFIGURATION _streamConfig;
|
||||
CONNECTION_LISTENER_CALLBACKS _clCallbacks;
|
||||
DECODER_RENDERER_CALLBACKS _drCallbacks;
|
||||
AUDIO_RENDERER_CALLBACKS _arCallbacks;
|
||||
int _serverMajorVersion;
|
||||
}
|
||||
|
||||
static OpusDecoder *opusDecoder;
|
||||
@@ -44,10 +45,6 @@ static int audioBufferQueueLength;
|
||||
static AudioComponentInstance audioUnit;
|
||||
static VideoDecoderRenderer* renderer;
|
||||
|
||||
void DrSetup(int width, int height, int fps, void* context, int drFlags)
|
||||
{
|
||||
}
|
||||
|
||||
int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
|
||||
{
|
||||
int offset = 0;
|
||||
@@ -68,23 +65,16 @@ int DrSubmitDecodeUnit(PDECODE_UNIT decodeUnit)
|
||||
return [renderer submitDecodeBuffer:data length:decodeUnit->fullLength];
|
||||
}
|
||||
|
||||
void DrStart(void)
|
||||
{
|
||||
}
|
||||
|
||||
void DrStop(void)
|
||||
{
|
||||
}
|
||||
|
||||
void DrRelease(void)
|
||||
{
|
||||
}
|
||||
|
||||
void ArInit(void)
|
||||
void ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig)
|
||||
{
|
||||
int err;
|
||||
|
||||
opusDecoder = opus_decoder_create(48000, 2, &err);
|
||||
// We only support stereo for now
|
||||
assert(audioConfiguration == AUDIO_CONFIGURATION_STEREO);
|
||||
|
||||
opusDecoder = opus_decoder_create(opusConfig->sampleRate,
|
||||
opusConfig->channelCount,
|
||||
&err);
|
||||
|
||||
audioLock = [[NSLock alloc] init];
|
||||
|
||||
@@ -92,9 +82,9 @@ void ArInit(void)
|
||||
NSError *audioSessionError = nil;
|
||||
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
|
||||
|
||||
[audioSession setPreferredSampleRate:48000.0 error:&audioSessionError];
|
||||
[audioSession setPreferredSampleRate:opusConfig->sampleRate error:&audioSessionError];
|
||||
[audioSession setCategory: AVAudioSessionCategoryPlayback error: &audioSessionError];
|
||||
[audioSession setPreferredOutputNumberOfChannels:2 error:&audioSessionError];
|
||||
[audioSession setPreferredOutputNumberOfChannels:opusConfig->channelCount error:&audioSessionError];
|
||||
[audioSession setPreferredIOBufferDuration:0.005 error:&audioSessionError];
|
||||
[audioSession setActive: YES error: &audioSessionError];
|
||||
|
||||
@@ -110,15 +100,15 @@ void ArInit(void)
|
||||
status = AudioComponentInstanceNew(AudioComponentFindNext(NULL, &audioDesc), &audioUnit);
|
||||
|
||||
if (status) {
|
||||
NSLog(@"Unable to instantiate new AudioComponent: %d", (int32_t)status);
|
||||
Log(LOG_E, @"Unable to instantiate new AudioComponent: %d", (int32_t)status);
|
||||
}
|
||||
|
||||
AudioStreamBasicDescription audioFormat = {0};
|
||||
audioFormat.mSampleRate = 48000;
|
||||
audioFormat.mSampleRate = opusConfig->sampleRate;
|
||||
audioFormat.mBitsPerChannel = 16;
|
||||
audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
||||
audioFormat.mChannelsPerFrame = 2;
|
||||
audioFormat.mChannelsPerFrame = opusConfig->channelCount;
|
||||
audioFormat.mBytesPerFrame = audioFormat.mChannelsPerFrame * (audioFormat.mBitsPerChannel / 8);
|
||||
audioFormat.mBytesPerPacket = audioFormat.mBytesPerFrame;
|
||||
audioFormat.mFramesPerPacket = audioFormat.mBytesPerPacket / audioFormat.mBytesPerFrame;
|
||||
@@ -131,7 +121,7 @@ void ArInit(void)
|
||||
&audioFormat,
|
||||
sizeof(audioFormat));
|
||||
if (status) {
|
||||
NSLog(@"Unable to set audio unit to input: %d", (int32_t)status);
|
||||
Log(LOG_E, @"Unable to set audio unit to input: %d", (int32_t)status);
|
||||
}
|
||||
|
||||
AURenderCallbackStruct callbackStruct = {0};
|
||||
@@ -145,25 +135,35 @@ void ArInit(void)
|
||||
&callbackStruct,
|
||||
sizeof(callbackStruct));
|
||||
if (status) {
|
||||
NSLog(@"Unable to set audio unit callback: %d", (int32_t)status);
|
||||
Log(LOG_E, @"Unable to set audio unit callback: %d", (int32_t)status);
|
||||
}
|
||||
|
||||
status = AudioUnitInitialize(audioUnit);
|
||||
if (status) {
|
||||
NSLog(@"Unable to initialize audioUnit: %d", (int32_t)status);
|
||||
Log(LOG_E, @"Unable to initialize audioUnit: %d", (int32_t)status);
|
||||
}
|
||||
|
||||
status = AudioOutputUnitStart(audioUnit);
|
||||
if (status) {
|
||||
Log(LOG_E, @"Unable to start audioUnit: %d", (int32_t)status);
|
||||
}
|
||||
}
|
||||
|
||||
void ArRelease(void)
|
||||
void ArCleanup(void)
|
||||
{
|
||||
if (opusDecoder != NULL) {
|
||||
opus_decoder_destroy(opusDecoder);
|
||||
opusDecoder = NULL;
|
||||
}
|
||||
|
||||
OSStatus status = AudioUnitUninitialize(audioUnit);
|
||||
OSStatus status = AudioOutputUnitStop(audioUnit);
|
||||
if (status) {
|
||||
NSLog(@"Unable to uninitialize audioUnit: %d", (int32_t)status);
|
||||
Log(LOG_E, @"Unable to stop audioUnit: %d", (int32_t)status);
|
||||
}
|
||||
|
||||
status = AudioUnitUninitialize(audioUnit);
|
||||
if (status) {
|
||||
Log(LOG_E, @"Unable to uninitialize audioUnit: %d", (int32_t)status);
|
||||
}
|
||||
|
||||
// Audio session is now inactive
|
||||
@@ -181,22 +181,6 @@ void ArRelease(void)
|
||||
}
|
||||
}
|
||||
|
||||
void ArStart(void)
|
||||
{
|
||||
OSStatus status = AudioOutputUnitStart(audioUnit);
|
||||
if (status) {
|
||||
NSLog(@"Unable to start audioUnit: %d", (int32_t)status);
|
||||
}
|
||||
}
|
||||
|
||||
void ArStop(void)
|
||||
{
|
||||
OSStatus status = AudioOutputUnitStop(audioUnit);
|
||||
if (status) {
|
||||
NSLog(@"Unable to stop audioUnit: %d", (int32_t)status);
|
||||
}
|
||||
}
|
||||
|
||||
void ArDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||
{
|
||||
int decodedLength = opus_decode(opusDecoder, (unsigned char*)sampleData, sampleLength, decodedPcmBuffer, PCM_BUFFER_SIZE / 2, 0);
|
||||
@@ -213,7 +197,7 @@ void ArDecodeAndPlaySample(char* sampleData, int sampleLength)
|
||||
|
||||
[audioLock lock];
|
||||
if (audioBufferQueueLength > MAX_QUEUE_ENTRIES) {
|
||||
NSLog(@"Audio player too slow. Dropping all decoded samples!");
|
||||
Log(LOG_W, @"Audio player too slow. Dropping all decoded samples!");
|
||||
|
||||
// Clear all values from the buffer queue
|
||||
struct AUDIO_BUFFER_QUEUE_ENTRY *entry;
|
||||
@@ -289,17 +273,20 @@ void ClDisplayTransientMessage(char* message)
|
||||
|
||||
-(void) terminate
|
||||
{
|
||||
// We're guaranteed to not be on a limelight-common thread
|
||||
// We're guaranteed to not be on a moonlight-common thread
|
||||
// here so it's safe to call stop directly
|
||||
LiStopConnection();
|
||||
}
|
||||
|
||||
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks
|
||||
-(id) initWithConfig:(StreamConfiguration*)config renderer:(VideoDecoderRenderer*)myRenderer connectionCallbacks:(id<ConnectionCallbacks>)callbacks serverMajorVersion:(int)serverMajorVersion
|
||||
{
|
||||
self = [super init];
|
||||
_host = config.hostAddr;
|
||||
_host = [config.host cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
renderer = myRenderer;
|
||||
_callbacks = callbacks;
|
||||
_serverMajorVersion = serverMajorVersion;
|
||||
|
||||
LiInitializeStreamConfiguration(&_streamConfig);
|
||||
_streamConfig.width = config.width;
|
||||
_streamConfig.height = config.height;
|
||||
_streamConfig.fps = config.frameRate;
|
||||
@@ -313,18 +300,15 @@ void ClDisplayTransientMessage(char* message)
|
||||
int riKeyId = htonl(config.riKeyId);
|
||||
memcpy(_streamConfig.remoteInputAesIv, &riKeyId, sizeof(riKeyId));
|
||||
|
||||
_drCallbacks.setup = DrSetup;
|
||||
_drCallbacks.start = DrStart;
|
||||
_drCallbacks.stop = DrStop;
|
||||
_drCallbacks.release = DrRelease;
|
||||
LiInitializeVideoCallbacks(&_drCallbacks);
|
||||
_drCallbacks.submitDecodeUnit = DrSubmitDecodeUnit;
|
||||
|
||||
LiInitializeAudioCallbacks(&_arCallbacks);
|
||||
_arCallbacks.init = ArInit;
|
||||
_arCallbacks.start = ArStart;
|
||||
_arCallbacks.stop = ArStop;
|
||||
_arCallbacks.release = ArRelease;
|
||||
_arCallbacks.cleanup = ArCleanup;
|
||||
_arCallbacks.decodeAndPlaySample = ArDecodeAndPlaySample;
|
||||
|
||||
LiInitializeConnectionCallbacks(&_clCallbacks);
|
||||
_clCallbacks.stageStarting = ClStageStarting;
|
||||
_clCallbacks.stageComplete = ClStageComplete;
|
||||
_clCallbacks.stageFailed = ClStageFailed;
|
||||
@@ -419,21 +403,12 @@ static OSStatus playbackCallback(void *inRefCon,
|
||||
|
||||
-(void) main
|
||||
{
|
||||
PLATFORM_CALLBACKS dummyPlCallbacks;
|
||||
|
||||
// Only used on Windows
|
||||
dummyPlCallbacks.threadStart = NULL;
|
||||
dummyPlCallbacks.debugPrint = NULL;
|
||||
|
||||
LiStartConnection(_host,
|
||||
&_streamConfig,
|
||||
&_clCallbacks,
|
||||
&_drCallbacks,
|
||||
&_arCallbacks,
|
||||
&dummyPlCallbacks,
|
||||
NULL, 0);
|
||||
NULL, 0, _serverMajorVersion);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// StreamConfiguration.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
@property NSString* host;
|
||||
@property NSString* appID;
|
||||
@property int hostAddr;
|
||||
@property int width;
|
||||
@property int height;
|
||||
@property int frameRate;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
//
|
||||
// StreamConfiguration.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "StreamConfiguration.h"
|
||||
|
||||
@implementation StreamConfiguration
|
||||
@synthesize host, appID, hostAddr, width, height, frameRate, bitRate, riKeyId, riKey;
|
||||
@synthesize host, appID, width, height, frameRate, bitRate, riKeyId, riKey;
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// StreamManager.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// StreamManager.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "StreamManager.h"
|
||||
@@ -12,6 +12,10 @@
|
||||
#import "Utils.h"
|
||||
#import "OnScreenControls.h"
|
||||
#import "StreamView.h"
|
||||
#import "ServerInfoResponse.h"
|
||||
#import "HttpResponse.h"
|
||||
#import "HttpRequest.h"
|
||||
#import "IdManager.h"
|
||||
|
||||
@implementation StreamManager {
|
||||
StreamConfiguration* _config;
|
||||
@@ -33,7 +37,7 @@
|
||||
|
||||
- (void)main {
|
||||
[CryptoManager generateKeyPairUsingSSl];
|
||||
NSString* uniqueId = [CryptoManager getUniqueID];
|
||||
NSString* uniqueId = [IdManager getUniqueId];
|
||||
NSData* cert = [CryptoManager readCertFromFile];
|
||||
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_config.host
|
||||
@@ -41,11 +45,13 @@
|
||||
deviceName:@"roth"
|
||||
cert:cert];
|
||||
|
||||
NSData* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
|
||||
NSString* currentGame = [HttpManager getStringFromXML:serverInfoResp tag:@"currentgame"];
|
||||
NSString* pairStatus = [HttpManager getStringFromXML:serverInfoResp tag:@"PairStatus"];
|
||||
NSString* currentClient = [HttpManager getStringFromXML:serverInfoResp tag:@"CurrentClient"];
|
||||
if (currentGame == NULL || pairStatus == NULL) {
|
||||
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]
|
||||
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
||||
NSString* pairStatus = [serverInfoResp getStringTag:@"PairStatus"];
|
||||
NSString* appversion = [serverInfoResp getStringTag:@"appversion"];
|
||||
NSString* serverState = [serverInfoResp getStringTag:@"state"];
|
||||
if (![serverInfoResp isStatusOk] || pairStatus == NULL || appversion == NULL || serverState == NULL) {
|
||||
[_callbacks launchFailed:@"Failed to connect to PC"];
|
||||
return;
|
||||
}
|
||||
@@ -57,12 +63,7 @@
|
||||
}
|
||||
|
||||
// resumeApp and launchApp handle calling launchFailed
|
||||
if (![currentGame isEqualToString:@"0"]) {
|
||||
if (![currentClient isEqualToString:@"1"]) {
|
||||
// The server is streaming to someone else
|
||||
[_callbacks launchFailed:@"There is another stream in progress"];
|
||||
return;
|
||||
}
|
||||
if ([serverState hasSuffix:@"_SERVER_BUSY"]) {
|
||||
// App already running, resume it
|
||||
if (![self resumeApp:hMan]) {
|
||||
return;
|
||||
@@ -81,40 +82,46 @@
|
||||
[((StreamView*)_renderView) setMouseDeltaFactors:_config.width / screenSize.width
|
||||
y:_config.height / screenSize.height];
|
||||
|
||||
int majorVersion = [[appversion substringToIndex:1] intValue];
|
||||
Log(LOG_I, @"Server is generation %d", majorVersion);
|
||||
|
||||
VideoDecoderRenderer* renderer = [[VideoDecoderRenderer alloc]initWithView:_renderView];
|
||||
_connection = [[Connection alloc] initWithConfig:_config renderer:renderer connectionCallbacks:_callbacks];
|
||||
_connection = [[Connection alloc] initWithConfig:_config renderer:renderer connectionCallbacks:_callbacks serverMajorVersion:majorVersion];
|
||||
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
|
||||
[opQueue addOperation:_connection];
|
||||
}
|
||||
|
||||
// This should NEVER be called from within a thread
|
||||
// owned by limelight-common
|
||||
// owned by moonlight-common
|
||||
- (void) stopStream
|
||||
{
|
||||
[_connection terminate];
|
||||
}
|
||||
|
||||
// This should only be called from within a thread
|
||||
// owned by limelight-common
|
||||
// owned by moonlight-common
|
||||
- (void) stopStreamInternal
|
||||
{
|
||||
[_connection terminateInternal];
|
||||
}
|
||||
|
||||
- (BOOL) launchApp:(HttpManager*)hMan {
|
||||
NSData* launchResp = [hMan executeRequestSynchronously:
|
||||
HttpResponse* launchResp = [[HttpResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:launchResp withUrlRequest:
|
||||
[hMan newLaunchRequest:_config.appID
|
||||
width:_config.width
|
||||
height:_config.height
|
||||
refreshRate:_config.frameRate
|
||||
rikey:[Utils bytesToHex:_config.riKey]
|
||||
rikeyid:_config.riKeyId]];
|
||||
NSString *gameSession = [HttpManager getStringFromXML:launchResp tag:@"gamesession"];
|
||||
if (launchResp == NULL) {
|
||||
rikeyid:_config.riKeyId]]];
|
||||
NSString *gameSession = [launchResp getStringTag:@"gamesession"];
|
||||
if (launchResp == NULL || ![launchResp isStatusOk]) {
|
||||
[_callbacks launchFailed:@"Failed to launch app"];
|
||||
Log(LOG_E, @"Failed Launch Response: %@", launchResp.statusMessage);
|
||||
return FALSE;
|
||||
} else if (gameSession == NULL || [gameSession isEqualToString:@"0"]) {
|
||||
[_callbacks launchFailed:[HttpManager getStatusStringFromXML:launchResp]];
|
||||
[_callbacks launchFailed:launchResp.statusMessage];
|
||||
Log(LOG_E, @"Failed to parse game session. Code: %ld Response: %@", (long)launchResp.statusCode, launchResp.statusMessage);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -122,15 +129,18 @@
|
||||
}
|
||||
|
||||
- (BOOL) resumeApp:(HttpManager*)hMan {
|
||||
NSData* resumeResp = [hMan executeRequestSynchronously:
|
||||
HttpResponse* resumeResp = [[HttpResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:resumeResp withUrlRequest:
|
||||
[hMan newResumeRequestWithRiKey:[Utils bytesToHex:_config.riKey]
|
||||
riKeyId:_config.riKeyId]];
|
||||
NSString *resume = [HttpManager getStringFromXML:resumeResp tag:@"resume"];
|
||||
if (resumeResp == NULL) {
|
||||
riKeyId:_config.riKeyId]]];
|
||||
NSString* resume = [resumeResp getStringTag:@"resume"];
|
||||
if (resumeResp == NULL || ![resumeResp isStatusOk]) {
|
||||
[_callbacks launchFailed:@"Failed to resume app"];
|
||||
Log(LOG_E, @"Failed Resume Response: %@", resumeResp.statusMessage);
|
||||
return FALSE;
|
||||
} else if (resume == NULL || [resume isEqualToString:@"0"]) {
|
||||
[_callbacks launchFailed:[HttpManager getStatusStringFromXML:resumeResp]];
|
||||
[_callbacks launchFailed:resumeResp.statusMessage];
|
||||
Log(LOG_E, @"Failed to parse resume response. Code: %ld Response: %@", (long)resumeResp.statusCode, resumeResp.statusMessage);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// VideoDecoderRenderer.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/18/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// VideoDecoderRenderer.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 10/18/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VideoDecoderRenderer.h"
|
||||
@@ -86,7 +86,7 @@
|
||||
kCFAllocatorDefault,
|
||||
NULL, 0, nalLength + 1, 0);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
status = CMBlockBufferReplaceDataBytes(lengthBytes, existingBuffer,
|
||||
oldOffset, NAL_LENGTH_PREFIX_SIZE);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
@@ -106,7 +106,7 @@
|
||||
kCFAllocatorDefault, NULL, 0,
|
||||
NAL_LENGTH_PREFIX_SIZE, 0);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMBlockBufferAppendMemoryBlock failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMBlockBufferAppendMemoryBlock failed: %d", (int)status);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
status = CMBlockBufferReplaceDataBytes(lengthBytes, existingBuffer,
|
||||
oldOffset, NAL_LENGTH_PREFIX_SIZE);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@
|
||||
kCFAllocatorNull, // Don't deallocate data on free
|
||||
NULL, 0, dataLength, 0);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMBlockBufferReplaceDataBytes failed: %d", (int)status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@
|
||||
|
||||
// Check for previous decoder errors before doing anything
|
||||
if (displayLayer.status == AVQueuedSampleBufferRenderingStatusFailed) {
|
||||
NSLog(@"Display layer rendering failed: %@", displayLayer.error);
|
||||
Log(LOG_E, @"Display layer rendering failed: %@", displayLayer.error);
|
||||
|
||||
// Recreate the display layer
|
||||
[self reinitializeDisplayLayer];
|
||||
@@ -152,14 +152,14 @@
|
||||
|
||||
if (nalType == NAL_TYPE_SPS || nalType == NAL_TYPE_PPS) {
|
||||
if (nalType == NAL_TYPE_SPS) {
|
||||
NSLog(@"Got SPS");
|
||||
Log(LOG_I, @"Got SPS");
|
||||
spsData = [NSData dataWithBytes:&data[FRAME_START_PREFIX_SIZE] length:length - FRAME_START_PREFIX_SIZE];
|
||||
waitingForSps = false;
|
||||
|
||||
// We got a new SPS so wait for a new PPS to match it
|
||||
waitingForPps = true;
|
||||
} else if (nalType == NAL_TYPE_PPS) {
|
||||
NSLog(@"Got PPS");
|
||||
Log(LOG_I, @"Got PPS");
|
||||
ppsData = [NSData dataWithBytes:&data[FRAME_START_PREFIX_SIZE] length:length - FRAME_START_PREFIX_SIZE];
|
||||
waitingForPps = false;
|
||||
}
|
||||
@@ -169,7 +169,7 @@
|
||||
const uint8_t* const parameterSetPointers[] = { [spsData bytes], [ppsData bytes] };
|
||||
const size_t parameterSetSizes[] = { [spsData length], [ppsData length] };
|
||||
|
||||
NSLog(@"Constructing new format description");
|
||||
Log(LOG_I, @"Constructing new format description");
|
||||
status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
|
||||
2, /* count of parameter sets */
|
||||
parameterSetPointers,
|
||||
@@ -177,7 +177,7 @@
|
||||
NAL_LENGTH_PREFIX_SIZE,
|
||||
&formatDesc);
|
||||
if (status != noErr) {
|
||||
NSLog(@"Failed to create format description: %d", (int)status);
|
||||
Log(LOG_E, @"Failed to create format description: %d", (int)status);
|
||||
formatDesc = NULL;
|
||||
}
|
||||
}
|
||||
@@ -206,7 +206,7 @@
|
||||
|
||||
status = CMBlockBufferCreateEmpty(NULL, 0, 0, &blockBuffer);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMBlockBufferCreateEmpty failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMBlockBufferCreateEmpty failed: %d", (int)status);
|
||||
free(data);
|
||||
return DR_NEED_IDR;
|
||||
}
|
||||
@@ -241,7 +241,7 @@
|
||||
NULL, 0, NULL,
|
||||
&sampleBuffer);
|
||||
if (status != noErr) {
|
||||
NSLog(@"CMSampleBufferCreate failed: %d", (int)status);
|
||||
Log(LOG_E, @"CMSampleBufferCreate failed: %d", (int)status);
|
||||
CFRelease(blockBuffer);
|
||||
return DR_NEED_IDR;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
//
|
||||
// UIAppView.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/22/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "App.h"
|
||||
#import "TemporaryApp.h"
|
||||
|
||||
@protocol AppCallback <NSObject>
|
||||
|
||||
- (void) appClicked:(App*) app;
|
||||
- (void) appClicked:(TemporaryApp*) app;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIAppView : UIView
|
||||
|
||||
- (id) initWithApp:(App*)app andCallback:(id<AppCallback>)callback;
|
||||
- (id) initWithApp:(TemporaryApp*)app cache:(NSCache*)cache andCallback:(id<AppCallback>)callback;
|
||||
- (void) updateAppImage;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,62 +1,108 @@
|
||||
//
|
||||
// UIAppView.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/22/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIAppView.h"
|
||||
|
||||
@implementation UIAppView {
|
||||
App* _app;
|
||||
TemporaryApp* _app;
|
||||
UIButton* _appButton;
|
||||
UILabel* _appLabel;
|
||||
UIImageView* _appOverlay;
|
||||
NSCache* _artCache;
|
||||
id<AppCallback> _callback;
|
||||
}
|
||||
static int LABEL_DY = 20;
|
||||
|
||||
- (id) initWithApp:(App*)app andCallback:(id<AppCallback>)callback {
|
||||
static UIImage* noImage;
|
||||
|
||||
- (id) initWithApp:(TemporaryApp*)app cache:(NSCache*)cache andCallback:(id<AppCallback>)callback {
|
||||
self = [super init];
|
||||
_app = app;
|
||||
_callback = callback;
|
||||
_artCache = cache;
|
||||
|
||||
// Cache the NoAppImage ourselves to avoid
|
||||
// having to load it each time
|
||||
if (noImage == nil) {
|
||||
noImage = [UIImage imageNamed:@"NoAppImage"];
|
||||
}
|
||||
|
||||
_appButton = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
UIImage* noImage = [UIImage imageNamed:@"NoAppImage"];
|
||||
[_appButton setBackgroundImage:noImage forState:UIControlStateNormal];
|
||||
[_appButton setContentEdgeInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
|
||||
[_appButton setContentEdgeInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
|
||||
[_appButton sizeToFit];
|
||||
[_appButton addTarget:self action:@selector(appClicked) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
[self addSubview:_appButton];
|
||||
[self sizeToFit];
|
||||
|
||||
_appButton.frame = CGRectMake(0, 0, noImage.size.width, noImage.size.height);
|
||||
self.frame = CGRectMake(0, 0, noImage.size.width, noImage.size.height);
|
||||
|
||||
// Rasterizing the cell layer increases rendering performance by quite a bit
|
||||
self.layer.shouldRasterize = YES;
|
||||
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) updateBounds {
|
||||
float x = _appButton.frame.origin.x < _appLabel.frame.origin.x ? _appButton.frame.origin.x : _appLabel.frame.origin.x;
|
||||
float y = _appButton.frame.origin.y < _appLabel.frame.origin.y ? _appButton.frame.origin.y : _appLabel.frame.origin.y;
|
||||
self.bounds = CGRectMake(x , y, _appButton.frame.size.width > _appLabel.frame.size.width ? _appButton.frame.size.width : _appLabel.frame.size.width, _appButton.frame.size.height + _appLabel.frame.size.height + LABEL_DY / 2);
|
||||
self.frame = CGRectMake(x , y, _appButton.frame.size.width > _appLabel.frame.size.width ? _appButton.frame.size.width : _appLabel.frame.size.width, _appButton.frame.size.height + _appLabel.frame.size.height + LABEL_DY / 2);
|
||||
}
|
||||
|
||||
- (void) appClicked {
|
||||
[_callback appClicked:_app];
|
||||
}
|
||||
|
||||
- (void) updateAppImage {
|
||||
if (_app.appImage != nil) {
|
||||
_appButton.frame = CGRectMake(0, 0, _app.appImage.size.width / 2, _app.appImage.size.height / 2);
|
||||
self.frame = CGRectMake(0, 0, _app.appImage.size.width / 2, _app.appImage.size.height / 2);
|
||||
[_appButton setBackgroundImage:_app.appImage forState:UIControlStateNormal];
|
||||
[self setNeedsDisplay];
|
||||
if (_appOverlay != nil) {
|
||||
[_appOverlay removeFromSuperview];
|
||||
_appOverlay = nil;
|
||||
}
|
||||
|
||||
|
||||
if (_app.isRunning) {
|
||||
// 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)];
|
||||
}
|
||||
|
||||
// TODO: Improve no-app image detection
|
||||
if (_app.appImage == nil || (_app.appImage.size.width == 130.f && _app.appImage.size.height == 180.f)) { // This size of image might be blank image received from GameStream.
|
||||
BOOL noAppImage = false;
|
||||
|
||||
if (_app.image != nil) {
|
||||
// Load the decoded image from the cache
|
||||
UIImage* appImage = [_artCache objectForKey:_app];
|
||||
if (appImage == nil) {
|
||||
// Not cached; we have to decode this now
|
||||
appImage = [UIImage imageWithData:_app.image];
|
||||
[_artCache setObject:appImage forKey:_app];
|
||||
}
|
||||
|
||||
// This size of image might be blank image received from GameStream.
|
||||
if (!(appImage.size.width == 130.f && appImage.size.height == 180.f)) {
|
||||
_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)];
|
||||
[_appButton setBackgroundImage:appImage forState:UIControlStateNormal];
|
||||
[self setNeedsDisplay];
|
||||
} else {
|
||||
noAppImage = true;
|
||||
}
|
||||
} else {
|
||||
noAppImage = true;
|
||||
}
|
||||
|
||||
if (noAppImage) {
|
||||
_appLabel = [[UILabel alloc] init];
|
||||
CGFloat padding = 4.f;
|
||||
[_appLabel setFrame: CGRectMake(padding, padding, _appButton.frame.size.width - 2 * padding, _appButton.frame.size.height - 2 * padding)];
|
||||
@@ -66,18 +112,10 @@ static int LABEL_DY = 20;
|
||||
[_appLabel setTextAlignment:NSTextAlignmentCenter];
|
||||
[_appLabel setLineBreakMode:NSLineBreakByWordWrapping];
|
||||
[_appLabel setNumberOfLines:0];
|
||||
[_appLabel setText:_app.appName];
|
||||
[_appLabel setText:_app.name];
|
||||
[_appButton addSubview:_appLabel];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// Only override drawRect: if you perform custom drawing.
|
||||
// An empty implementation adversely affects performance during animation.
|
||||
- (void)drawRect:(CGRect)rect {
|
||||
// Drawing code
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
//
|
||||
// UIComputerView.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/22/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "Host.h"
|
||||
#import "TemporaryHost.h"
|
||||
|
||||
@protocol HostCallback <NSObject>
|
||||
|
||||
- (void) hostClicked:(Host*)host;
|
||||
- (void) hostLongClicked:(Host*)host view:(UIView*)view;
|
||||
- (void) hostClicked:(TemporaryHost*)host view:(UIView*)view;
|
||||
- (void) hostLongClicked:(TemporaryHost*)host view:(UIView*)view;
|
||||
- (void) addHostClicked;
|
||||
|
||||
@end
|
||||
|
||||
@interface UIComputerView : UIView
|
||||
|
||||
- (id) initWithComputer:(Host*)host andCallback:(id<HostCallback>)callback;
|
||||
- (id) initWithComputer:(TemporaryHost*)host andCallback:(id<HostCallback>)callback;
|
||||
- (id) initForAddWithCallback:(id<HostCallback>)callback;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
//
|
||||
// UIComputerView.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/22/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIComputerView.h"
|
||||
|
||||
@implementation UIComputerView {
|
||||
Host* _host;
|
||||
TemporaryHost* _host;
|
||||
UIButton* _hostButton;
|
||||
UILabel* _hostLabel;
|
||||
UILabel* _hostStatus;
|
||||
@@ -72,7 +72,7 @@ static const int LABEL_DY = 20;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithComputer:(Host*)host andCallback:(id<HostCallback>)callback {
|
||||
- (id) initWithComputer:(TemporaryHost*)host andCallback:(id<HostCallback>)callback {
|
||||
self = [self init];
|
||||
_host = host;
|
||||
_callback = callback;
|
||||
@@ -123,7 +123,7 @@ static const int LABEL_DY = 20;
|
||||
self.frame = CGRectMake(x, y, width, height);
|
||||
}
|
||||
|
||||
- (void) updateContentsForHost:(Host*)host {
|
||||
- (void) updateContentsForHost:(TemporaryHost*)host {
|
||||
_hostLabel.text = _host.name;
|
||||
_hostLabel.textColor = [UIColor whiteColor];
|
||||
[_hostLabel sizeToFit];
|
||||
@@ -171,7 +171,7 @@ static const int LABEL_DY = 20;
|
||||
}
|
||||
|
||||
- (void) hostClicked {
|
||||
[_callback hostClicked:_host];
|
||||
[_callback hostClicked:_host view:self];
|
||||
}
|
||||
|
||||
- (void) addClicked {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Logger.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/10/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Limelight_Logger_h
|
||||
#define Limelight_Logger_h
|
||||
|
||||
#import <stdarg.h>
|
||||
|
||||
typedef enum {
|
||||
LOG_D,
|
||||
LOG_I,
|
||||
LOG_W,
|
||||
LOG_E
|
||||
} LogLevel;
|
||||
|
||||
#define PRFX_DEBUG @"<DEBUG>"
|
||||
#define PRFX_INFO @"<INFO>"
|
||||
#define PRFX_WARN @"<WARN>"
|
||||
#define PRFX_ERROR @"<ERROR>"
|
||||
|
||||
void Log(LogLevel level, NSString* fmt, ...);
|
||||
void LogTag(LogLevel level, NSString* tag, NSString* fmt, ...);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// Logger.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/10/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Logger.h"
|
||||
|
||||
void LogTagv(LogLevel level, NSString* tag, NSString* fmt, va_list args);
|
||||
|
||||
void Log(LogLevel level, NSString* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
LogTagv(level, NULL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogTag(LogLevel level, NSString* tag, NSString* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
LogTagv(level, tag, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void LogTagv(LogLevel level, NSString* tag, NSString* fmt, va_list args) {
|
||||
NSString* levelPrefix = @"";
|
||||
|
||||
switch(level) {
|
||||
case LOG_D:
|
||||
levelPrefix = PRFX_DEBUG;
|
||||
break;
|
||||
case LOG_I:
|
||||
levelPrefix = PRFX_INFO;
|
||||
break;
|
||||
case LOG_W:
|
||||
levelPrefix = PRFX_WARN;
|
||||
break;
|
||||
case LOG_E:
|
||||
levelPrefix = PRFX_ERROR;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
NSString* prefixedString;
|
||||
if (tag) {
|
||||
prefixedString = [NSString stringWithFormat:@"%@ (%@) %@", levelPrefix, tag, fmt];
|
||||
} else {
|
||||
prefixedString = [NSString stringWithFormat:@"%@ %@", levelPrefix, fmt];
|
||||
}
|
||||
NSLogv(prefixedString, args);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Utils.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
@@ -24,3 +24,9 @@ FOUNDATION_EXPORT NSString *const deviceName;
|
||||
+ (int) resolveHost:(NSString*)host;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSString (NSStringWithTrim)
|
||||
|
||||
- (NSString*) trim;
|
||||
|
||||
@end
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// Utils.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/20/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Utils.h"
|
||||
@@ -56,22 +56,29 @@ NSString *const deviceName = @"roth";
|
||||
if (inet_addr([host UTF8String]) != INADDR_NONE) {
|
||||
// Already an IP address
|
||||
int addr = inet_addr([host UTF8String]);
|
||||
NSLog(@"host address: %d", addr);
|
||||
Log(LOG_I, @"host address: %d", addr);
|
||||
return addr;
|
||||
} else {
|
||||
hostent = gethostbyname([host UTF8String]);
|
||||
if (hostent != NULL) {
|
||||
char* ipstr = inet_ntoa(*(struct in_addr*)hostent->h_addr_list[0]);
|
||||
NSLog(@"Resolved %@ -> %s", host, ipstr);
|
||||
Log(LOG_I, @"Resolved %@ -> %s", host, ipstr);
|
||||
int addr = inet_addr(ipstr);
|
||||
NSLog(@"host address: %d", addr);
|
||||
Log(LOG_I, @"host address: %d", addr);
|
||||
return addr;
|
||||
} else {
|
||||
NSLog(@"Failed to resolve host: %d", h_errno);
|
||||
Log(LOG_W, @"Failed to resolve host: %d", h_errno);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSString (NSStringWithTrim)
|
||||
|
||||
- (NSString *)trim {
|
||||
return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// LoadingFrameViewController.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/24/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface LoadingFrameViewController : UIViewController
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *loadingSpinner;
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// LoadingFrameViewController.m
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 2/24/15.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LoadingFrameViewController.h"
|
||||
|
||||
@interface LoadingFrameViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation LoadingFrameViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// center the loading spinner
|
||||
self.loadingSpinner.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2);
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
}
|
||||
|
||||
/*
|
||||
#pragma mark - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
// Get the new view controller using [segue destinationViewController].
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// MainFrameViewController.h
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/17/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@@ -12,13 +12,12 @@
|
||||
#import "StreamConfiguration.h"
|
||||
#import "UIComputerView.h"
|
||||
#import "UIAppView.h"
|
||||
#import "AppManager.h"
|
||||
#import "AppAssetManager.h"
|
||||
#import "SWRevealViewController.h"
|
||||
|
||||
@interface MainFrameViewController : UICollectionViewController <DiscoveryCallback, PairCallback, HostCallback, AppCallback, AppAssetCallback, NSURLConnectionDelegate, SWRevealViewControllerDelegate>
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UIButton *limelightLogoButton;
|
||||
@property (weak, nonatomic) IBOutlet UIBarButtonItem *computerNameButton;
|
||||
|
||||
+ (StreamConfiguration*) getStreamConfiguration;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,98 +1,234 @@
|
||||
// MainFrameViewController.m
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/17/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
@import ImageIO;
|
||||
|
||||
#import "MainFrameViewController.h"
|
||||
#import "CryptoManager.h"
|
||||
#import "HttpManager.h"
|
||||
#import "Connection.h"
|
||||
#import "VideoDecoderRenderer.h"
|
||||
#import "StreamManager.h"
|
||||
#import "Utils.h"
|
||||
#import "UIComputerView.h"
|
||||
#import "UIAppView.h"
|
||||
#import "App.h"
|
||||
#import "SettingsViewController.h"
|
||||
#import "DataManager.h"
|
||||
#import "Settings.h"
|
||||
#import "TemporarySettings.h"
|
||||
#import "WakeOnLanManager.h"
|
||||
#import "AppListResponse.h"
|
||||
#import "ServerInfoResponse.h"
|
||||
#import "StreamFrameViewController.h"
|
||||
#import "LoadingFrameViewController.h"
|
||||
#import "ComputerScrollView.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "IdManager.h"
|
||||
|
||||
@implementation MainFrameViewController {
|
||||
NSOperationQueue* _opQueue;
|
||||
Host* _selectedHost;
|
||||
TemporaryHost* _selectedHost;
|
||||
NSString* _uniqueId;
|
||||
NSData* _cert;
|
||||
NSString* _currentGame;
|
||||
DiscoveryManager* _discMan;
|
||||
AppManager* _appManager;
|
||||
UIAlertView* _pairAlert;
|
||||
AppAssetManager* _appManager;
|
||||
StreamConfiguration* _streamConfig;
|
||||
UIAlertController* _pairAlert;
|
||||
UIScrollView* hostScrollView;
|
||||
int currentPosition;
|
||||
NSArray* _sortedAppList;
|
||||
NSCache* _boxArtCache;
|
||||
}
|
||||
static NSMutableSet* hostList;
|
||||
static NSArray* appList;
|
||||
static StreamConfiguration* streamConfig;
|
||||
|
||||
+ (StreamConfiguration*) getStreamConfiguration {
|
||||
return streamConfig;
|
||||
}
|
||||
|
||||
- (void)showPIN:(NSString *)PIN {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
_pairAlert = [[UIAlertView alloc] initWithTitle:@"Pairing" message:[NSString stringWithFormat:@"Enter the following PIN on the host machine: %@", PIN]delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
|
||||
[_pairAlert show];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_pairAlert = [UIAlertController alertControllerWithTitle:@"Pairing"
|
||||
message:[NSString stringWithFormat:@"Enter the following PIN on the host machine: %@", PIN]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[self presentViewController:_pairAlert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pairFailed:(NSString *)message {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[_pairAlert dismissWithClickedButtonIndex:0 animated:NO];
|
||||
_pairAlert = [[UIAlertView alloc] initWithTitle:@"Pairing Failed" message:message delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
|
||||
[_pairAlert show];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_pairAlert dismissViewControllerAnimated:YES completion:nil];
|
||||
_pairAlert = [UIAlertController alertControllerWithTitle:@"Pairing Failed"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[_pairAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||
[self presentViewController:_pairAlert animated:YES completion:nil];
|
||||
|
||||
[_discMan startDiscovery];
|
||||
[self hideLoadingFrame];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)pairSuccessful {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[_pairAlert dismissWithClickedButtonIndex:0 animated:NO];
|
||||
_pairAlert = [[UIAlertView alloc] initWithTitle:@"Pairing Succesful" message:@"Successfully paired to host" delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
|
||||
[_pairAlert show];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_pairAlert dismissViewControllerAnimated:YES completion:nil];
|
||||
[_discMan startDiscovery];
|
||||
[self alreadyPaired];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)alreadyPaired {
|
||||
BOOL usingCachedAppList = false;
|
||||
|
||||
// Capture the host here because it can change once we
|
||||
// leave the main thread
|
||||
TemporaryHost* host = _selectedHost;
|
||||
|
||||
if ([host.appList count] > 0) {
|
||||
usingCachedAppList = true;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (host != _selectedHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
_computerNameButton.title = host.name;
|
||||
[self.navigationController.navigationBar setNeedsLayout];
|
||||
|
||||
[self updateAppsForHost:host];
|
||||
[self hideLoadingFrame];
|
||||
});
|
||||
}
|
||||
Log(LOG_I, @"Using cached app list: %d", usingCachedAppList);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_computerNameButton.title = _selectedHost.name;
|
||||
[self.navigationController.navigationBar setNeedsLayout];
|
||||
});
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* appListResp = [hMan executeRequestSynchronously:[hMan newAppListRequest]];
|
||||
appList = [HttpManager getAppListFromXML:appListResp];
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateApps];
|
||||
});
|
||||
// Exempt this host from discovery while handling the applist query
|
||||
[_discMan removeHostFromDiscovery:host];
|
||||
|
||||
[_appManager stopRetrieving];
|
||||
[_appManager retrieveAssets:appList fromHost:_selectedHost];
|
||||
// Try up to 5 times to get the app list
|
||||
AppListResponse* appListResp;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
appListResp = [[AppListResponse alloc] init];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:appListResp withUrlRequest:[hMan newAppListRequest]]];
|
||||
if (appListResp == nil || ![appListResp isStatusOk] || [appListResp getAppList] == nil) {
|
||||
Log(LOG_W, @"Failed to get applist on try %d: %@", i, appListResp.statusMessage);
|
||||
|
||||
// Wait for one second then retry
|
||||
[NSThread sleepForTimeInterval:1];
|
||||
}
|
||||
else {
|
||||
Log(LOG_I, @"App list successfully retreived - took %d tries", i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[_discMan addHostToDiscovery:host];
|
||||
|
||||
if (appListResp == nil || ![appListResp isStatusOk] || [appListResp getAppList] == nil) {
|
||||
Log(LOG_W, @"Failed to get applist: %@", appListResp.statusMessage);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self hideLoadingFrame];
|
||||
|
||||
if (host != _selectedHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIAlertController* applistAlert = [UIAlertController alertControllerWithTitle:@"Fetching App List Failed"
|
||||
message:@"The connection to the PC was interrupted."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[applistAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||
[self presentViewController:applistAlert animated:YES completion:nil];
|
||||
host.online = NO;
|
||||
[self showHostSelectionView];
|
||||
});
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateApplist:[appListResp getAppList] forHost:host];
|
||||
|
||||
if (host != _selectedHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
_computerNameButton.title = host.name;
|
||||
[self.navigationController.navigationBar setNeedsLayout];
|
||||
|
||||
[self updateAppsForHost:host];
|
||||
[_appManager stopRetrieving];
|
||||
[_appManager retrieveAssetsFromHost:host];
|
||||
[self hideLoadingFrame];
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void) updateApplist:(NSSet*) newList forHost:(TemporaryHost*)host {
|
||||
DataManager* database = [[DataManager alloc] init];
|
||||
|
||||
for (TemporaryApp* app in newList) {
|
||||
BOOL appAlreadyInList = NO;
|
||||
for (TemporaryApp* savedApp in host.appList) {
|
||||
if ([app.id isEqualToString:savedApp.id]) {
|
||||
savedApp.name = app.name;
|
||||
savedApp.isRunning = app.isRunning;
|
||||
appAlreadyInList = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!appAlreadyInList) {
|
||||
app.host = host;
|
||||
[host.appList addObject:app];
|
||||
}
|
||||
}
|
||||
|
||||
BOOL appWasRemoved;
|
||||
do {
|
||||
appWasRemoved = NO;
|
||||
|
||||
for (TemporaryApp* app in host.appList) {
|
||||
appWasRemoved = YES;
|
||||
for (TemporaryApp* mergedApp in newList) {
|
||||
if ([mergedApp.id isEqualToString:app.id]) {
|
||||
appWasRemoved = NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (appWasRemoved) {
|
||||
// Removing the app mutates the list we're iterating (which isn't legal).
|
||||
// We need to jump out of this loop and restart enumeration.
|
||||
|
||||
[host.appList removeObject:app];
|
||||
|
||||
// It's important to remove the app record from the database
|
||||
// since we'll have a constraint violation now that appList
|
||||
// doesn't have this app in it.
|
||||
[database removeApp:app];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep looping until the list is no longer being mutated
|
||||
} while (appWasRemoved);
|
||||
|
||||
[database updateAppsForExistingHost:host];
|
||||
}
|
||||
|
||||
- (void)showHostSelectionView {
|
||||
appList = [[NSArray alloc] init];
|
||||
[_appManager stopRetrieving];
|
||||
_selectedHost = nil;
|
||||
_computerNameButton.title = @"No Host Selected";
|
||||
[self.collectionView reloadData];
|
||||
[self.view addSubview:hostScrollView];
|
||||
}
|
||||
|
||||
- (void) receivedAssetForApp:(App*)app {
|
||||
[self.collectionView reloadData];
|
||||
- (void) receivedAssetForApp:(TemporaryApp*)app {
|
||||
// Update the box art cache now so we don't have to do it
|
||||
// on the main thread
|
||||
[self updateBoxArtCacheForApp:app];
|
||||
|
||||
DataManager* dataManager = [[DataManager alloc] init];
|
||||
[dataManager updateIconForExistingApp: app];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.collectionView reloadData];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)displayDnsFailedDialog {
|
||||
@@ -103,36 +239,76 @@ static StreamConfiguration* streamConfig;
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void) hostClicked:(Host *)host {
|
||||
NSLog(@"Clicked host: %@", host.name);
|
||||
- (void) hostClicked:(TemporaryHost *)host view:(UIView *)view {
|
||||
// Treat clicks on offline hosts to be long clicks
|
||||
// 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) {
|
||||
[self hostLongClicked:host view:view];
|
||||
return;
|
||||
}
|
||||
|
||||
Log(LOG_D, @"Clicked host: %@", host.name);
|
||||
_selectedHost = host;
|
||||
[self disableNavigation];
|
||||
|
||||
// If we are online, paired, and have a cached app list, skip straight
|
||||
// to the app grid without a loading frame. This is the fast path that users
|
||||
// should hit most.
|
||||
if (host.online && host.pairState == PairStatePaired && host.appList.count > 0) {
|
||||
[self alreadyPaired];
|
||||
return;
|
||||
}
|
||||
|
||||
[self showLoadingFrame];
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
NSData* serverInfoResp = [hMan executeRequestSynchronously:[hMan newServerInfoRequest]];
|
||||
if ([[HttpManager getStringFromXML:serverInfoResp tag:@"PairStatus"] isEqualToString:@"1"]) {
|
||||
NSLog(@"Already Paired");
|
||||
[self alreadyPaired];
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
|
||||
|
||||
// Exempt this host from discovery while handling the serverinfo request
|
||||
[_discMan removeHostFromDiscovery:host];
|
||||
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]
|
||||
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
|
||||
[_discMan addHostToDiscovery:host];
|
||||
|
||||
if (serverInfoResp == nil || ![serverInfoResp isStatusOk]) {
|
||||
Log(LOG_W, @"Failed to get server info: %@", serverInfoResp.statusMessage);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self hideLoadingFrame];
|
||||
|
||||
if (host != _selectedHost) {
|
||||
return;
|
||||
}
|
||||
|
||||
UIAlertController* applistAlert = [UIAlertController alertControllerWithTitle:@"Fetching Server Info Failed"
|
||||
message:@"The connection to the PC was interrupted."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[applistAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||
[self presentViewController:applistAlert animated:YES completion:nil];
|
||||
host.online = NO;
|
||||
[self showHostSelectionView];
|
||||
});
|
||||
} else {
|
||||
NSLog(@"Trying to pair");
|
||||
// Polling the server while pairing causes the server to screw up
|
||||
[_discMan stopDiscoveryBlocking];
|
||||
PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:_cert callback:self];
|
||||
[_opQueue addOperation:pMan];
|
||||
Log(LOG_D, @"server info pair status: %@", [serverInfoResp getStringTag:@"PairStatus"]);
|
||||
if ([[serverInfoResp getStringTag:@"PairStatus"] isEqualToString:@"1"]) {
|
||||
Log(LOG_I, @"Already Paired");
|
||||
[self alreadyPaired];
|
||||
} else {
|
||||
Log(LOG_I, @"Trying to pair");
|
||||
// Polling the server while pairing causes the server to screw up
|
||||
[_discMan stopDiscoveryBlocking];
|
||||
PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:_cert callback:self];
|
||||
[_opQueue addOperation:pMan];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (void)hostLongClicked:(Host *)host view:(UIView *)view {
|
||||
NSLog(@"Long clicked host: %@", host.name);
|
||||
- (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) {
|
||||
[longClickAlert addAction:[UIAlertAction actionWithTitle:@"Unpair" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:host.address uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
[hMan executeRequestSynchronously:[hMan newUnpairRequest]];
|
||||
});
|
||||
}]];
|
||||
} else {
|
||||
if (!host.online) {
|
||||
[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]];
|
||||
@@ -155,15 +331,15 @@ static StreamConfiguration* streamConfig;
|
||||
[dataMan removeHost:host];
|
||||
@synchronized(hostList) {
|
||||
[hostList removeObject:host];
|
||||
[self updateAllHosts:[hostList allObjects]];
|
||||
}
|
||||
[self updateAllHosts:[hostList allObjects]];
|
||||
|
||||
}]];
|
||||
[longClickAlert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||
|
||||
// these two lines are required for iPad support of UIAlertSheet
|
||||
longClickAlert.popoverPresentationController.sourceView = view;
|
||||
|
||||
|
||||
longClickAlert.popoverPresentationController.sourceRect = CGRectMake(view.bounds.size.width / 2.0, view.bounds.size.height / 2.0, 1.0, 1.0); // center of the view
|
||||
[self presentViewController:longClickAlert animated:YES completion:^{
|
||||
[self updateHosts];
|
||||
@@ -171,16 +347,15 @@ static StreamConfiguration* streamConfig;
|
||||
}
|
||||
|
||||
- (void) addHostClicked {
|
||||
NSLog(@"Clicked add host");
|
||||
Log(LOG_D, @"Clicked add host");
|
||||
[self showLoadingFrame];
|
||||
UIAlertController* alertController = [UIAlertController alertControllerWithTitle:@"Host Address" message:@"Please enter a hostname or IP address" preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
||||
NSString* hostAddress = ((UITextField*)[[alertController textFields] objectAtIndex:0]).text;
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
|
||||
[_discMan discoverHost:hostAddress withCallback:^(Host* host){
|
||||
[_discMan discoverHost:hostAddress withCallback:^(TemporaryHost* host, NSString* error){
|
||||
if (host != nil) {
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
[dataMan saveHosts];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@synchronized(hostList) {
|
||||
[hostList addObject:host];
|
||||
@@ -188,7 +363,7 @@ static StreamConfiguration* streamConfig;
|
||||
[self updateHosts];
|
||||
});
|
||||
} else {
|
||||
UIAlertController* hostNotFoundAlert = [UIAlertController alertControllerWithTitle:@"Host Not Found" message:[NSString stringWithFormat:@"Unable to connect to host: \n%@", hostAddress] preferredStyle:UIAlertControllerStyleAlert];
|
||||
UIAlertController* hostNotFoundAlert = [UIAlertController alertControllerWithTitle:@"Add Host" message:error preferredStyle:UIAlertControllerStyleAlert];
|
||||
[hostNotFoundAlert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self presentViewController:hostNotFoundAlert animated:YES completion:nil];
|
||||
@@ -197,59 +372,89 @@ static StreamConfiguration* streamConfig;
|
||||
}];});
|
||||
}]];
|
||||
[alertController addTextFieldWithConfigurationHandler:nil];
|
||||
[self hideLoadingFrame];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void) appClicked:(App *)app {
|
||||
NSLog(@"Clicked app: %@", app.appName);
|
||||
streamConfig = [[StreamConfiguration alloc] init];
|
||||
streamConfig.host = _selectedHost.address;
|
||||
streamConfig.hostAddr = [Utils resolveHost:_selectedHost.address];
|
||||
streamConfig.appID = app.appId;
|
||||
if (streamConfig.hostAddr == 0) {
|
||||
[self displayDnsFailedDialog];
|
||||
return;
|
||||
}
|
||||
- (void) appClicked:(TemporaryApp *)app {
|
||||
Log(LOG_D, @"Clicked app: %@", app.name);
|
||||
_streamConfig = [[StreamConfiguration alloc] init];
|
||||
_streamConfig.host = app.host.activeAddress;
|
||||
_streamConfig.appID = app.id;
|
||||
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
Settings* streamSettings = [dataMan retrieveSettings];
|
||||
TemporarySettings* streamSettings = [dataMan getSettings];
|
||||
|
||||
streamConfig.frameRate = [streamSettings.framerate intValue];
|
||||
streamConfig.bitRate = [streamSettings.bitrate intValue];
|
||||
streamConfig.height = [streamSettings.height intValue];
|
||||
streamConfig.width = [streamSettings.width intValue];
|
||||
_streamConfig.frameRate = [streamSettings.framerate intValue];
|
||||
_streamConfig.bitRate = [streamSettings.bitrate intValue];
|
||||
_streamConfig.height = [streamSettings.height intValue];
|
||||
_streamConfig.width = [streamSettings.width intValue];
|
||||
|
||||
[_appManager stopRetrieving];
|
||||
|
||||
if (currentPosition != FrontViewPositionLeft) {
|
||||
[[self revealViewController] revealToggle:self];
|
||||
}
|
||||
|
||||
App* currentApp = [self findRunningApp];
|
||||
TemporaryApp* currentApp = [self findRunningApp:app.host];
|
||||
if (currentApp != nil) {
|
||||
UIAlertController* alertController = [UIAlertController
|
||||
alertControllerWithTitle: app.appName
|
||||
message: [app.appId isEqualToString:currentApp.appId] ? @"" : [NSString stringWithFormat:@"%@ is currently running", currentApp.appName]preferredStyle:UIAlertControllerStyleAlert];
|
||||
alertControllerWithTitle: app.name
|
||||
message: [app.id isEqualToString:currentApp.id] ? @"" : [NSString stringWithFormat:@"%@ is currently running", currentApp.name]preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction
|
||||
actionWithTitle:[app.appId isEqualToString:currentApp.appId] ? @"Resume App" : @"Resume Running App" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
||||
NSLog(@"Resuming application: %@", currentApp.appName);
|
||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||
}]];
|
||||
actionWithTitle:[app.id isEqualToString:currentApp.id] ? @"Resume App" : @"Resume Running App" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
||||
Log(LOG_I, @"Resuming application: %@", currentApp.name);
|
||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:
|
||||
[app.appId isEqualToString:currentApp.appId] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||
NSLog(@"Quitting application: %@", currentApp.appName);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:_selectedHost.address uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
[hMan executeRequestSynchronously:[hMan newQuitAppRequest]];
|
||||
// TODO: handle failure to quit app
|
||||
currentApp.isRunning = NO;
|
||||
|
||||
if (![app.appId isEqualToString:currentApp.appId]) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||
});
|
||||
}
|
||||
});
|
||||
}]];
|
||||
[app.id isEqualToString:currentApp.id] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||
Log(LOG_I, @"Quitting application: %@", currentApp.name);
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
HttpManager* hMan = [[HttpManager alloc] initWithHost:app.host.activeAddress uniqueId:_uniqueId deviceName:deviceName cert:_cert];
|
||||
HttpResponse* quitResponse = [[HttpResponse alloc] init];
|
||||
HttpRequest* quitRequest = [HttpRequest requestForResponse: quitResponse withUrlRequest:[hMan newQuitAppRequest]];
|
||||
|
||||
// Exempt this host from discovery while handling the quit operation
|
||||
[_discMan removeHostFromDiscovery:app.host];
|
||||
[hMan executeRequestSynchronously:quitRequest];
|
||||
[_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"
|
||||
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]) {
|
||||
currentApp.isRunning = NO;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateAppsForHost:app.host];
|
||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
// Otherwise, display a dialog to notify the user that the app was quit
|
||||
else {
|
||||
currentApp.isRunning = NO;
|
||||
|
||||
alert = [UIAlertController alertControllerWithTitle:@"Quitting App"
|
||||
message:@"The app was quit successfully."
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
}
|
||||
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:nil]];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self updateAppsForHost:app.host];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
});
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
} else {
|
||||
@@ -257,9 +462,8 @@ static StreamConfiguration* streamConfig;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (App*) findRunningApp {
|
||||
for (App* app in appList) {
|
||||
- (TemporaryApp*) findRunningApp:(TemporaryHost*)host {
|
||||
for (TemporaryApp* app in host.appList) {
|
||||
if (app.isRunning) {
|
||||
return app;
|
||||
}
|
||||
@@ -275,14 +479,31 @@ static StreamConfiguration* streamConfig;
|
||||
currentPosition = position;
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([segue.destinationViewController isKindOfClass:[StreamFrameViewController class]]) {
|
||||
StreamFrameViewController* streamFrame = segue.destinationViewController;
|
||||
streamFrame.streamConfig = _streamConfig;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) showLoadingFrame {
|
||||
LoadingFrameViewController* loadingFrame = [self.storyboard instantiateViewControllerWithIdentifier:@"loadingFrame"];
|
||||
[self.navigationController presentViewController:loadingFrame animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void) hideLoadingFrame {
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[self enableNavigation];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// Set the side bar button action. When it's tapped, it'll show up the sidebar.
|
||||
// Set the side bar button action. When it's tapped, it'll show the sidebar.
|
||||
[_limelightLogoButton addTarget:self.revealViewController action:@selector(revealToggle:) forControlEvents:UIControlEventTouchDown];
|
||||
|
||||
// Set the host name button action. When it's tapped, it'll show up the host selection view.
|
||||
// Set the host name button action. When it's tapped, it'll show the host selection view.
|
||||
[_computerNameButton setTarget:self];
|
||||
[_computerNameButton setAction:@selector(showHostSelectionView)];
|
||||
|
||||
@@ -296,31 +517,66 @@ static StreamConfiguration* streamConfig;
|
||||
currentPosition = FrontViewPositionLeft;
|
||||
|
||||
// Set up crypto
|
||||
_opQueue = [[NSOperationQueue alloc] init];
|
||||
[CryptoManager generateKeyPairUsingSSl];
|
||||
_uniqueId = [CryptoManager getUniqueID];
|
||||
_uniqueId = [IdManager getUniqueId];
|
||||
_cert = [CryptoManager readCertFromFile];
|
||||
|
||||
_appManager = [[AppManager alloc] initWithCallback:self];
|
||||
|
||||
_appManager = [[AppAssetManager alloc] initWithCallback:self];
|
||||
_opQueue = [[NSOperationQueue alloc] init];
|
||||
|
||||
// Only initialize the host picker list once
|
||||
if (hostList == nil) {
|
||||
hostList = [[NSMutableSet alloc] init];
|
||||
}
|
||||
|
||||
_boxArtCache = [[NSCache alloc] init];
|
||||
|
||||
[self setAutomaticallyAdjustsScrollViewInsets:NO];
|
||||
|
||||
hostScrollView = [[UIScrollView alloc] init];
|
||||
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;
|
||||
|
||||
UIButton* pullArrow = [[UIButton alloc] init];
|
||||
[pullArrow setImage:[UIImage imageNamed:@"PullArrow"] forState:UIControlStateNormal];
|
||||
[pullArrow sizeToFit];
|
||||
pullArrow.frame = CGRectMake(0,
|
||||
self.collectionView.frame.size.height / 2 - pullArrow.frame.size.height / 2 - self.navigationController.navigationBar.frame.size.height,
|
||||
pullArrow.frame.size.width,
|
||||
pullArrow.frame.size.height);
|
||||
[self.view addSubview:pullArrow];
|
||||
|
||||
|
||||
self.collectionView.delaysContentTouches = NO;
|
||||
self.collectionView.allowsMultipleSelection = NO;
|
||||
self.collectionView.multipleTouchEnabled = NO;
|
||||
|
||||
[self retrieveSavedHosts];
|
||||
_discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver: self
|
||||
selector: @selector(handleReturnToForeground)
|
||||
name: UIApplicationWillEnterForegroundNotification
|
||||
object: nil];
|
||||
|
||||
[self updateHosts];
|
||||
[self.view addSubview:hostScrollView];
|
||||
}
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
-(void)handleReturnToForeground
|
||||
{
|
||||
// This will refresh the applist
|
||||
if (_selectedHost != nil) {
|
||||
[self hostClicked:_selectedHost view:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
@@ -333,10 +589,7 @@ static StreamConfiguration* streamConfig;
|
||||
|
||||
[_discMan startDiscovery];
|
||||
|
||||
// This will refresh the applist
|
||||
if (_selectedHost != nil) {
|
||||
[self hostClicked:_selectedHost];
|
||||
}
|
||||
[self handleReturnToForeground];
|
||||
}
|
||||
|
||||
- (void)viewDidDisappear:(BOOL)animated
|
||||
@@ -344,24 +597,37 @@ static StreamConfiguration* streamConfig;
|
||||
[super viewDidDisappear:animated];
|
||||
// when discovery stops, we must create a new instance because you cannot restart an NSOperation when it is finished
|
||||
[_discMan stopDiscovery];
|
||||
|
||||
// In case the host objects were updated in the background
|
||||
[[[DataManager alloc] init] saveHosts];
|
||||
|
||||
// Purge the box art cache
|
||||
[_boxArtCache removeAllObjects];
|
||||
}
|
||||
|
||||
- (void) retrieveSavedHosts {
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
NSArray* hosts = [dataMan retrieveHosts];
|
||||
NSArray* hosts = [dataMan getHosts];
|
||||
@synchronized(hostList) {
|
||||
[hostList addObjectsFromArray:hosts];
|
||||
|
||||
// Initialize the non-persistent host state
|
||||
for (TemporaryHost* host in hostList) {
|
||||
if (host.activeAddress == nil) {
|
||||
host.activeAddress = host.localAddress;
|
||||
}
|
||||
if (host.activeAddress == nil) {
|
||||
host.activeAddress = host.externalAddress;
|
||||
}
|
||||
if (host.activeAddress == nil) {
|
||||
host.activeAddress = host.address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateAllHosts:(NSArray *)hosts {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"New host list:");
|
||||
for (Host* host in hosts) {
|
||||
NSLog(@"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}", host.name, host.address, host.localAddress, host.externalAddress, host.uuid, host.mac, host.pairState, host.online);
|
||||
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);
|
||||
}
|
||||
@synchronized(hostList) {
|
||||
[hostList removeAllObjects];
|
||||
@@ -372,19 +638,22 @@ static StreamConfiguration* streamConfig;
|
||||
}
|
||||
|
||||
- (void)updateHosts {
|
||||
NSLog(@"Updating hosts...");
|
||||
Log(LOG_I, @"Updating hosts...");
|
||||
[[hostScrollView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
|
||||
UIComputerView* addComp = [[UIComputerView alloc] initForAddWithCallback:self];
|
||||
UIComputerView* compView;
|
||||
float prevEdge = -1;
|
||||
@synchronized (hostList) {
|
||||
for (Host* comp in hostList) {
|
||||
// Sort the host list in alphabetical order
|
||||
NSArray* sortedHostList = [[hostList allObjects] sortedArrayUsingSelector:@selector(compareName:)];
|
||||
for (TemporaryHost* comp in sortedHostList) {
|
||||
compView = [[UIComputerView alloc] initWithComputer:comp andCallback:self];
|
||||
compView.center = CGPointMake([self getCompViewX:compView addComp:addComp prevEdge:prevEdge], hostScrollView.frame.size.height / 2);
|
||||
prevEdge = compView.frame.origin.x + compView.frame.size.width;
|
||||
[hostScrollView addSubview:compView];
|
||||
}
|
||||
}
|
||||
|
||||
prevEdge = [self getCompViewX:addComp addComp:addComp prevEdge:prevEdge];
|
||||
addComp.center = CGPointMake(prevEdge, hostScrollView.frame.size.height / 2);
|
||||
|
||||
@@ -400,7 +669,66 @@ static StreamConfiguration* streamConfig;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateApps {
|
||||
// This function forces immediate decoding of the UIImage, rather
|
||||
// than the default lazy decoding that results in janky scrolling.
|
||||
+ (UIImage*) loadBoxArtForCaching:(TemporaryApp*)app {
|
||||
|
||||
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)app.image, NULL);
|
||||
CGImageRef cgImage = CGImageSourceCreateImageAtIndex(source, 0, (__bridge CFDictionaryRef)@{(id)kCGImageSourceShouldCacheImmediately: (id)kCFBooleanTrue});
|
||||
|
||||
UIImage *boxArt = [UIImage imageWithCGImage:cgImage];
|
||||
|
||||
CGImageRelease(cgImage);
|
||||
CFRelease(source);
|
||||
|
||||
return boxArt;
|
||||
}
|
||||
|
||||
- (void) updateBoxArtCacheForApp:(TemporaryApp*)app {
|
||||
if (app.image == nil) {
|
||||
[_boxArtCache removeObjectForKey:app];
|
||||
}
|
||||
else if ([_boxArtCache objectForKey:app] == nil) {
|
||||
[_boxArtCache setObject:[MainFrameViewController loadBoxArtForCaching:app] forKey:app];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) updateAppsForHost:(TemporaryHost*)host {
|
||||
if (host != _selectedHost) {
|
||||
Log(LOG_W, @"Mismatched host during app update");
|
||||
return;
|
||||
}
|
||||
|
||||
_sortedAppList = [host.appList allObjects];
|
||||
_sortedAppList = [_sortedAppList sortedArrayUsingSelector:@selector(compareName:)];
|
||||
|
||||
// Split the sorted array in half to allow 2 jobs to process app assets at once
|
||||
NSArray *firstHalf;
|
||||
NSArray *secondHalf;
|
||||
NSRange range;
|
||||
|
||||
range.location = 0;
|
||||
range.length = [_sortedAppList count] / 2;
|
||||
|
||||
firstHalf = [_sortedAppList subarrayWithRange:range];
|
||||
|
||||
range.location = range.length;
|
||||
range.length = [_sortedAppList count] - range.length;
|
||||
|
||||
secondHalf = [_sortedAppList subarrayWithRange:range];
|
||||
|
||||
// Start 2 jobs
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
for (TemporaryApp* app in firstHalf) {
|
||||
[self updateBoxArtCacheForApp:app];
|
||||
}
|
||||
});
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
for (TemporaryApp* app in secondHalf) {
|
||||
[self updateBoxArtCacheForApp:app];
|
||||
}
|
||||
});
|
||||
|
||||
[hostScrollView removeFromSuperview];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
@@ -408,8 +736,8 @@ static StreamConfiguration* streamConfig;
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UICollectionViewCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AppCell" forIndexPath:indexPath];
|
||||
|
||||
App* app = appList[indexPath.row];
|
||||
UIAppView* appView = [[UIAppView alloc] initWithApp:app andCallback:self];
|
||||
TemporaryApp* app = _sortedAppList[indexPath.row];
|
||||
UIAppView* appView = [[UIAppView alloc] initWithApp:app cache:_boxArtCache andCallback:self];
|
||||
[appView updateAppImage];
|
||||
|
||||
if (appView.bounds.size.width > 10.0) {
|
||||
@@ -420,7 +748,19 @@ static StreamConfiguration* streamConfig;
|
||||
|
||||
[cell.subviews.firstObject removeFromSuperview]; // Remove a view that was previously added
|
||||
[cell addSubview:appView];
|
||||
|
||||
|
||||
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRect:cell.bounds];
|
||||
cell.layer.masksToBounds = NO;
|
||||
cell.layer.shadowColor = [UIColor blackColor].CGColor;
|
||||
cell.layer.shadowOffset = CGSizeMake(1.0f, 5.0f);
|
||||
cell.layer.shadowOpacity = 0.5f;
|
||||
cell.layer.shadowPath = shadowPath.CGPath;
|
||||
|
||||
cell.layer.borderColor = [[UIColor colorWithRed:0 green:0 blue:0 alpha:0.3f] CGColor];
|
||||
cell.layer.borderWidth = 1;
|
||||
cell.exclusiveTouch = YES;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -429,7 +769,12 @@ static StreamConfiguration* streamConfig;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
|
||||
return appList.count;
|
||||
if (_selectedHost != nil) {
|
||||
return _selectedHost.appList.count;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
@@ -450,4 +795,13 @@ static StreamConfiguration* streamConfig;
|
||||
- (BOOL)shouldAutorotate {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) disableNavigation {
|
||||
self.navigationController.navigationBar.topItem.rightBarButtonItem.enabled = NO;
|
||||
}
|
||||
|
||||
- (void) enableNavigation {
|
||||
self.navigationController.navigationBar.topItem.rightBarButtonItem.enabled = YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -554,8 +554,8 @@ static CGFloat scaledValue( CGFloat v1, CGFloat min2, CGFloat max2, CGFloat min1
|
||||
UITouch *touch = [touches anyObject];
|
||||
CGPoint nowPoint = [touch locationInView:self.view];
|
||||
|
||||
if (abs(nowPoint.x - _beginPoint.x) > kDirectionPanThreshold) _dragging = YES;
|
||||
else if (abs(nowPoint.y - _beginPoint.y) > kDirectionPanThreshold) self.state = UIGestureRecognizerStateFailed;
|
||||
if (fabs(nowPoint.x - _beginPoint.x) > kDirectionPanThreshold) _dragging = YES;
|
||||
else if (fabs(nowPoint.y - _beginPoint.y) > kDirectionPanThreshold) self.state = UIGestureRecognizerStateFailed;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -742,15 +742,6 @@ const int FrontViewPositionNone = 0xff;
|
||||
_userInteractionStore = _contentView.userInteractionEnabled;
|
||||
}
|
||||
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
{
|
||||
// we could have simply not implemented this, but we choose to call super to make explicit that we
|
||||
// want the default behavior.
|
||||
return [super supportedInterfaceOrientations];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Public methods and property accessors
|
||||
|
||||
- (void)setFrontViewController:(UIViewController *)frontViewController
|
||||
@@ -1140,7 +1131,7 @@ const int FrontViewPositionNone = 0xff;
|
||||
// forbid gesture if the initial translation is not horizontal
|
||||
UIView *recognizerView = _panGestureRecognizer.view;
|
||||
CGPoint translation = [_panGestureRecognizer translationInView:recognizerView];
|
||||
// NSLog( @"translation:%@", NSStringFromCGPoint(translation) );
|
||||
// Log(LOG_D, @"translation:%@", NSStringFromCGPoint(translation) );
|
||||
// if ( fabs(translation.y/translation.x) > 1 )
|
||||
// return NO;
|
||||
|
||||
@@ -1252,7 +1243,7 @@ const int FrontViewPositionNone = 0xff;
|
||||
|
||||
CGFloat xLocation = frontView.frame.origin.x;
|
||||
CGFloat velocity = [recognizer velocityInView:_contentView].x;
|
||||
//NSLog( @"Velocity:%1.4f", velocity);
|
||||
//Log(LOG_D, @"Velocity:%1.4f", velocity);
|
||||
|
||||
// depending on position we compute a simetric replacement of widths and positions
|
||||
int symetry = xLocation<0 ? -1 : 1;
|
||||
@@ -1274,7 +1265,7 @@ const int FrontViewPositionNone = 0xff;
|
||||
NSTimeInterval duration = _toggleAnimationDuration;
|
||||
|
||||
// Velocity driven change:
|
||||
if (fabsf(velocity) > _quickFlickVelocity)
|
||||
if (fabs(velocity) > _quickFlickVelocity)
|
||||
{
|
||||
// we may need to set the drag position and to adjust the animation duration
|
||||
CGFloat journey = xLocation;
|
||||
@@ -1292,7 +1283,7 @@ const int FrontViewPositionNone = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
duration = fabsf(journey/velocity);
|
||||
duration = fabs(journey/velocity);
|
||||
}
|
||||
|
||||
// Position driven change:
|
||||
@@ -1349,7 +1340,7 @@ const int FrontViewPositionNone = 0xff;
|
||||
|
||||
NSTimeInterval duration = animated?_toggleAnimationDuration:0.0;
|
||||
NSTimeInterval firstDuration = duration;
|
||||
int initialPosDif = abs( _frontViewPosition - preReplacementPosition );
|
||||
int initialPosDif = (int) labs( _frontViewPosition - preReplacementPosition );
|
||||
if ( initialPosDif == 1 ) firstDuration *= 0.8;
|
||||
else if ( initialPosDif == 0 ) firstDuration = 0;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// SettingsViewController.h
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/27/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
//
|
||||
// SettingsViewController.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 10/27/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SettingsViewController.h"
|
||||
#import "Settings.h"
|
||||
#import "TemporarySettings.h"
|
||||
#import "DataManager.h"
|
||||
|
||||
#define BITRATE_INTERVAL 1
|
||||
#define BITRATE_INTERVAL 500 // in kbps
|
||||
|
||||
@implementation SettingsViewController {
|
||||
NSInteger _bitrate;
|
||||
}
|
||||
static NSString* bitrateFormat = @"Bitrate: %d Mbps";
|
||||
static NSString* bitrateFormat = @"Bitrate: %.1f Mbps";
|
||||
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
Settings* currentSettings = [dataMan retrieveSettings];
|
||||
TemporarySettings* currentSettings = [dataMan getSettings];
|
||||
|
||||
// Bitrate is persisted in kbps
|
||||
_bitrate = [currentSettings.bitrate integerValue] / 1000;
|
||||
_bitrate = [currentSettings.bitrate integerValue];
|
||||
NSInteger framerate = [currentSettings.framerate integerValue] == 30 ? 0 : 1;
|
||||
NSInteger resolution;
|
||||
if ([currentSettings.height integerValue] == 720) {
|
||||
@@ -33,43 +33,71 @@ static NSString* bitrateFormat = @"Bitrate: %d Mbps";
|
||||
} else if ([currentSettings.height integerValue] == 1080) {
|
||||
resolution = 1;
|
||||
} else {
|
||||
resolution = 2;
|
||||
resolution = 0;
|
||||
}
|
||||
NSInteger onscreenControls = [currentSettings.onscreenControls integerValue];
|
||||
|
||||
[self.resolutionSelector setSelectedSegmentIndex:resolution];
|
||||
[self.resolutionSelector addTarget:self action:@selector(newResolutionFpsChosen) forControlEvents:UIControlEventValueChanged];
|
||||
[self.framerateSelector setSelectedSegmentIndex:framerate];
|
||||
[self.framerateSelector addTarget:self action:@selector(newResolutionFpsChosen) forControlEvents:UIControlEventValueChanged];
|
||||
[self.onscreenControlSelector setSelectedSegmentIndex:onscreenControls];
|
||||
[self.bitrateSlider setValue:_bitrate animated:YES];
|
||||
[self.bitrateSlider setValue:(_bitrate / BITRATE_INTERVAL) animated:YES];
|
||||
[self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged];
|
||||
[self.bitrateLabel setText:[NSString stringWithFormat:bitrateFormat, (int)_bitrate]];
|
||||
[self updateBitrateText];
|
||||
}
|
||||
|
||||
- (void) newResolutionFpsChosen {
|
||||
NSInteger frameRate = [self getChosenFrameRate];
|
||||
NSInteger resHeight = [self getChosenStreamHeight];
|
||||
NSInteger defaultBitrate;
|
||||
|
||||
// 1080p60 is 20 Mbps
|
||||
if (frameRate == 60 && resHeight == 1080) {
|
||||
defaultBitrate = 20000;
|
||||
}
|
||||
// 720p60 and 1080p30 are 10 Mbps
|
||||
else if (frameRate == 60 || resHeight == 1080) {
|
||||
defaultBitrate = 10000;
|
||||
}
|
||||
// 720p30 is 5 Mbps
|
||||
else {
|
||||
defaultBitrate = 5000;
|
||||
}
|
||||
|
||||
_bitrate = defaultBitrate;
|
||||
[self.bitrateSlider setValue:defaultBitrate / BITRATE_INTERVAL animated:YES];
|
||||
|
||||
[self updateBitrateText];
|
||||
}
|
||||
|
||||
- (void) bitrateSliderMoved {
|
||||
_bitrate = BITRATE_INTERVAL * floor((self.bitrateSlider.value/BITRATE_INTERVAL)+0.5);
|
||||
[self.bitrateLabel setText:[NSString stringWithFormat:bitrateFormat, (int)_bitrate]];
|
||||
_bitrate = BITRATE_INTERVAL * (int)self.bitrateSlider.value;
|
||||
[self updateBitrateText];
|
||||
}
|
||||
|
||||
- (void) updateBitrateText {
|
||||
// Display bitrate in Mbps
|
||||
[self.bitrateLabel setText:[NSString stringWithFormat:bitrateFormat, _bitrate / 1000.]];
|
||||
}
|
||||
|
||||
- (NSInteger) getChosenFrameRate {
|
||||
return [self.framerateSelector selectedSegmentIndex] == 0 ? 30 : 60;
|
||||
}
|
||||
|
||||
- (NSInteger) getChosenStreamHeight {
|
||||
return [self.resolutionSelector selectedSegmentIndex] == 0 ? 720 : 1080;
|
||||
}
|
||||
|
||||
- (NSInteger) getChosenStreamWidth {
|
||||
return [self getChosenStreamHeight] == 720 ? 1280 : 1920;
|
||||
}
|
||||
|
||||
- (void) saveSettings {
|
||||
DataManager* dataMan = [[DataManager alloc] init];
|
||||
NSInteger framerate = [self.framerateSelector selectedSegmentIndex] == 0 ? 30 : 60;
|
||||
NSInteger height;
|
||||
NSInteger width;
|
||||
if ([self.resolutionSelector selectedSegmentIndex] == 2) {
|
||||
// Get screen native resolution
|
||||
height = [UIScreen mainScreen].bounds.size.height * [UIScreen mainScreen].scale;
|
||||
width = [UIScreen mainScreen].bounds.size.width * [UIScreen mainScreen].scale;
|
||||
|
||||
UIAlertController *alertResolution = [UIAlertController alertControllerWithTitle:@"Native resolution" message:[NSString stringWithFormat:@"You must select the following resolution in the game settings: %lix%li", (long)width, (long)height] preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertResolution addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[self presentViewController:alertResolution animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
height = [self.resolutionSelector selectedSegmentIndex] == 0 ? 720 : 1080;
|
||||
width = height == 720 ? 1280 : 1920;
|
||||
}
|
||||
NSInteger framerate = [self getChosenFrameRate];
|
||||
NSInteger height = [self getChosenStreamHeight];
|
||||
NSInteger width = [self getChosenStreamWidth];
|
||||
NSInteger onscreenControls = [self.onscreenControlSelector selectedSegmentIndex];
|
||||
[dataMan saveSettingsWithBitrate:_bitrate framerate:framerate height:height width:width onscreenControls:onscreenControls];
|
||||
}
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
//
|
||||
// StreamFrameViewController.h
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/18/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Connection.h"
|
||||
#import "StreamConfiguration.h"
|
||||
#import "StreamView.h"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks>
|
||||
@interface StreamFrameViewController : UIViewController <ConnectionCallbacks, EdgeDetectionDelegate>
|
||||
@property (strong, nonatomic) IBOutlet UILabel *stageLabel;
|
||||
@property (strong, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
|
||||
@property (nonatomic) StreamConfiguration* streamConfig;
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// StreamFrameViewController.m
|
||||
// Limelight-iOS
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 1/18/14.
|
||||
// Copyright (c) 2014 Diego Waxemberg. All rights reserved.
|
||||
// Copyright (c) 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import "StreamFrameViewController.h"
|
||||
@@ -11,7 +11,6 @@
|
||||
#import "VideoDecoderRenderer.h"
|
||||
#import "StreamManager.h"
|
||||
#import "ControllerSupport.h"
|
||||
#import "StreamView.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
@@ -36,7 +35,7 @@
|
||||
|
||||
_controllerSupport = [[ControllerSupport alloc] init];
|
||||
|
||||
_streamMan = [[StreamManager alloc] initWithConfig:[MainFrameViewController getStreamConfiguration]
|
||||
_streamMan = [[StreamManager alloc] initWithConfig:self.streamConfig
|
||||
renderView:self.view
|
||||
connectionCallbacks:self];
|
||||
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
|
||||
@@ -49,6 +48,7 @@
|
||||
}
|
||||
|
||||
- (void) returnToMainFrame {
|
||||
[_controllerSupport cleanup];
|
||||
[self.navigationController popToRootViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
@@ -58,30 +58,23 @@
|
||||
}
|
||||
|
||||
- (void)edgeSwiped {
|
||||
NSLog(@"User swiped to end stream");
|
||||
Log(LOG_D, @"User swiped to end stream");
|
||||
[_streamMan stopStream];
|
||||
[self returnToMainFrame];
|
||||
}
|
||||
|
||||
- (void) connectionStarted {
|
||||
NSLog(@"Connection started");
|
||||
Log(LOG_I, @"Connection started");
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.spinner stopAnimating];
|
||||
[self.stageLabel setText:@"Waiting for first frame..."];
|
||||
[self.stageLabel sizeToFit];
|
||||
[(StreamView*)self.view setupOnScreenControls: _controllerSupport];
|
||||
UIScreenEdgePanGestureRecognizer* swipeGesture = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)];
|
||||
swipeGesture.edges = UIRectEdgeLeft;
|
||||
if (swipeGesture == nil) {
|
||||
NSLog(@"An error occured trying to create UIScreenEdgePanGestureRecognizer");
|
||||
} else {
|
||||
[self.view addGestureRecognizer:swipeGesture];
|
||||
}
|
||||
[(StreamView*)self.view setupOnScreenControls: _controllerSupport swipeDelegate:self];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)connectionTerminated:(long)errorCode {
|
||||
NSLog(@"Connection terminated: %ld", errorCode);
|
||||
Log(LOG_I, @"Connection terminated: %ld", errorCode);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController* conTermAlert = [UIAlertController alertControllerWithTitle:@"Connection Terminated" message:@"The connection was terminated" preferredStyle:UIAlertControllerStyleAlert];
|
||||
@@ -95,7 +88,7 @@
|
||||
}
|
||||
|
||||
- (void) stageStarting:(char*)stageName {
|
||||
NSLog(@"Starting %s", stageName);
|
||||
Log(LOG_I, @"Starting %s", stageName);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSString* lowerCase = [NSString stringWithFormat:@"%s in progress...", stageName];
|
||||
NSString* titleCase = [[[lowerCase substringToIndex:1] uppercaseString] stringByAppendingString:[lowerCase substringFromIndex:1]];
|
||||
@@ -109,32 +102,42 @@
|
||||
}
|
||||
|
||||
- (void) stageFailed:(char*)stageName withError:(long)errorCode {
|
||||
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed"
|
||||
message:[NSString stringWithFormat:@"%s failed with error %ld",
|
||||
stageName, errorCode]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||
[self returnToMainFrame];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
Log(LOG_I, @"Stage %s failed: %ld", stageName, errorCode);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed"
|
||||
message:[NSString stringWithFormat:@"%s failed with error %ld",
|
||||
stageName, errorCode]
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||
[self returnToMainFrame];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
|
||||
[_streamMan stopStreamInternal];
|
||||
}
|
||||
|
||||
- (void) launchFailed:(NSString*)message {
|
||||
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||
[self returnToMainFrame];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
Log(LOG_I, @"Launch failed: %@", message);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed"
|
||||
message:message
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||
[self returnToMainFrame];
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
|
||||
- (void) displayMessage:(char*)message {
|
||||
NSLog(@"Display message: %s", message);
|
||||
Log(LOG_I, @"Display message: %s", message);
|
||||
}
|
||||
|
||||
- (void) displayTransientMessage:(char*)message {
|
||||
NSLog(@"Display transient message: %s", message);
|
||||
Log(LOG_I, @"Display transient message: %s", message);
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//
|
||||
// main.m
|
||||
// Limelight
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Diego Waxemberg on 8/30/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@@ -1,22 +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>com.limelight-stream.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</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,34 +0,0 @@
|
||||
//
|
||||
// LimelightTests.m
|
||||
// LimelightTests
|
||||
//
|
||||
// Created by Diego Waxemberg on 8/30/14.
|
||||
// Copyright (c) 2014 Limelight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface LimelightTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation LimelightTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testExample
|
||||
{
|
||||
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,2 +0,0 @@
|
||||
/* Localized versions of Info.plist keys */
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objectVersion = 47;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
98A03B4D19F352EB00861ACA /* liblimelight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98A03B4A19F3514B00861ACA /* liblimelight-common.a */; };
|
||||
9832D1361BBCD5C50036EF48 /* TemporaryApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 9832D1351BBCD5C50036EF48 /* TemporaryApp.m */; };
|
||||
987140041B04542F00AB57D5 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB1E43101AE8B0F200AFF679 /* 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 */; };
|
||||
9E5D600B1A5A5A3900689918 /* Apache License.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D5FF81A5A5A3900689918 /* Apache License.txt */; };
|
||||
9E5D600C1A5A5A3900689918 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D5FF91A5A5A3900689918 /* Roboto-Black.ttf */; };
|
||||
9E5D600E1A5A5A3900689918 /* Roboto-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D5FFB1A5A5A3900689918 /* Roboto-Bold.ttf */; };
|
||||
@@ -15,6 +18,8 @@
|
||||
9E5D60131A5A5A3900689918 /* Roboto-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D60001A5A5A3900689918 /* Roboto-Medium.ttf */; };
|
||||
9E5D60151A5A5A3900689918 /* Roboto-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D60021A5A5A3900689918 /* Roboto-Regular.ttf */; };
|
||||
9E5D60161A5A5A3900689918 /* Roboto-Thin.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D60031A5A5A3900689918 /* Roboto-Thin.ttf */; };
|
||||
FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */; };
|
||||
FB1D599A1BBCCD7E00F482CA /* AppCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */; };
|
||||
FB290CF219B2C406004C83CF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF119B2C406004C83CF /* Foundation.framework */; };
|
||||
FB290CF419B2C406004C83CF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF319B2C406004C83CF /* CoreGraphics.framework */; };
|
||||
FB290CF619B2C406004C83CF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF519B2C406004C83CF /* UIKit.framework */; };
|
||||
@@ -24,11 +29,6 @@
|
||||
FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = FB290D0319B2C406004C83CF /* AppDelegate.m */; };
|
||||
FB290D0719B2C406004C83CF /* Limelight.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */; };
|
||||
FB290D0919B2C406004C83CF /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = FB290D0819B2C406004C83CF /* Images.xcassets */; };
|
||||
FB290D1119B2C406004C83CF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF119B2C406004C83CF /* Foundation.framework */; };
|
||||
FB290D1219B2C406004C83CF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF519B2C406004C83CF /* UIKit.framework */; };
|
||||
FB290D1319B2C406004C83CF /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290CF719B2C406004C83CF /* CoreData.framework */; };
|
||||
FB290D1B19B2C406004C83CF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = FB290D1919B2C406004C83CF /* InfoPlist.strings */; };
|
||||
FB290D1D19B2C406004C83CF /* LimelightTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FB290D1C19B2C406004C83CF /* LimelightTests.m */; };
|
||||
FB290DB719B2C870004C83CF /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290DB619B2C870004C83CF /* libz.dylib */; };
|
||||
FB290DB919B2C877004C83CF /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290DB819B2C877004C83CF /* libbz2.dylib */; };
|
||||
FB290DC419B2E98F004C83CF /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB290DC319B2E98F004C83CF /* libxml2.dylib */; };
|
||||
@@ -38,6 +38,8 @@
|
||||
FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678F91A55FFAD00377732 /* DiscoveryManager.m */; };
|
||||
FB4678FF1A565DAC00377732 /* WakeOnLanManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */; };
|
||||
FB4679011A57048000377732 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB4679001A57048000377732 /* CoreFoundation.framework */; };
|
||||
FB4A23B81A9D3637004D2EF2 /* LoadingFrameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4A23B71A9D3637004D2EF2 /* LoadingFrameViewController.m */; };
|
||||
FB53E1431BE5DC4400CD6ECE /* IdManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB53E1421BE5DC4400CD6ECE /* IdManager.m */; };
|
||||
FB6549561A57907E001C8F39 /* DiscoveryWorker.m in Sources */ = {isa = PBXBuildFile; fileRef = FB6549551A57907E001C8F39 /* DiscoveryWorker.m */; };
|
||||
FB7E794419C8B71B00A15F68 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FB7E794319C8B71B00A15F68 /* libiconv.dylib */; };
|
||||
FB89462819F646E200339C8A /* CryptoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FB89460619F646E200339C8A /* CryptoManager.m */; };
|
||||
@@ -58,43 +60,50 @@
|
||||
FB8946EB19F6AFE100339C8A /* libcrypto.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946E019F6AFB800339C8A /* libcrypto.a */; };
|
||||
FB8946EC19F6AFE400339C8A /* libssl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946E119F6AFB800339C8A /* libssl.a */; };
|
||||
FB8946ED19F6AFE800339C8A /* libopus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB8946EA19F6AFB800339C8A /* libopus.a */; };
|
||||
FBD3494319FC9C04002D2A60 /* AppManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494219FC9C04002D2A60 /* AppManager.m */; };
|
||||
FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD271A7C84ED00872C98 /* HttpResponse.m */; };
|
||||
FB9AFD321A7D867C00872C98 /* AppAssetRetriever.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD311A7D867C00872C98 /* AppAssetRetriever.m */; };
|
||||
FB9AFD371A7E02DB00872C98 /* HttpRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD361A7E02DB00872C98 /* HttpRequest.m */; };
|
||||
FB9AFD3A1A7E05CE00872C98 /* ServerInfoResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD391A7E05CE00872C98 /* ServerInfoResponse.m */; };
|
||||
FB9AFD3D1A7E111600872C98 /* AppAssetResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD3C1A7E111600872C98 /* AppAssetResponse.m */; };
|
||||
FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD3F1A7E127D00872C98 /* AppListResponse.m */; };
|
||||
FB9AFD431A7F0C6900872C98 /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = FB9AFD421A7F0C6900872C98 /* Controller.m */; };
|
||||
FBD1C8E21A8AD71400C6703C /* Logger.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD1C8E11A8AD71400C6703C /* Logger.m */; };
|
||||
FBD3494319FC9C04002D2A60 /* AppAssetManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494219FC9C04002D2A60 /* AppAssetManager.m */; };
|
||||
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3494F19FF2174002D2A60 /* SettingsViewController.m */; };
|
||||
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */; };
|
||||
FBD3495B1A004411002D2A60 /* Host.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495A1A004411002D2A60 /* Host.m */; };
|
||||
FBD3495E1A004412002D2A60 /* Settings.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD3495D1A004412002D2A60 /* Settings.m */; };
|
||||
FBD349621A0089F6002D2A60 /* DataManager.m in Sources */ = {isa = PBXBuildFile; fileRef = FBD349611A0089F6002D2A60 /* DataManager.m */; };
|
||||
FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86DF19F7A837001C18A8 /* UIComputerView.m */; };
|
||||
FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E519F82297001C18A8 /* UIAppView.m */; };
|
||||
FBDE86E919F82315001C18A8 /* App.m in Sources */ = {isa = PBXBuildFile; fileRef = FBDE86E819F82315001C18A8 /* App.m */; };
|
||||
FBFCB3311B50B29400089F8A /* Host.m in Sources */ = {isa = PBXBuildFile; fileRef = FBFCB3301B50B29400089F8A /* Host.m */; };
|
||||
FBFCB3351B50B29400089F8A /* App.m in Sources */ = {isa = PBXBuildFile; fileRef = FBFCB3341B50B29400089F8A /* App.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
98A03B4919F3514B00861ACA /* PBXContainerItemProxy */ = {
|
||||
987140011B04541A00AB57D5 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 98A03B4519F3514B00861ACA /* limelight-common.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = FB290E2E19B37A4E004C83CF;
|
||||
remoteInfo = "limelight-common";
|
||||
};
|
||||
98A03B4B19F3517100861ACA /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 98A03B4519F3514B00861ACA /* limelight-common.xcodeproj */;
|
||||
containerPortal = 98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = FB290E2D19B37A4E004C83CF;
|
||||
remoteInfo = "limelight-common";
|
||||
remoteInfo = "moonlight-common";
|
||||
};
|
||||
FB290D1419B2C406004C83CF /* PBXContainerItemProxy */ = {
|
||||
FB1E430F1AE8B0F200AFF679 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = FB290CE619B2C406004C83CF /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = FB290CED19B2C406004C83CF;
|
||||
remoteInfo = Limelight;
|
||||
containerPortal = 98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = FB290E2E19B37A4E004C83CF;
|
||||
remoteInfo = "moonlight-common";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
98A03B4519F3514B00861ACA /* limelight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "limelight-common.xcodeproj"; path = "limelight-common-c/limelight-common.xcodeproj"; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "moonlight-common.xcodeproj"; path = "limelight-common-c/moonlight-common.xcodeproj"; sourceTree = "<group>"; };
|
||||
98D5856B1C0EA79600F6CC00 /* TemporaryHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryHost.h; path = Database/TemporaryHost.h; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
9E5D5FF81A5A5A3900689918 /* Apache License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Apache License.txt"; sourceTree = "<group>"; };
|
||||
9E5D5FF91A5A5A3900689918 /* Roboto-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Black.ttf"; sourceTree = "<group>"; };
|
||||
9E5D5FFB1A5A5A3900689918 /* Roboto-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Bold.ttf"; sourceTree = "<group>"; };
|
||||
@@ -102,7 +111,11 @@
|
||||
9E5D60001A5A5A3900689918 /* Roboto-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Medium.ttf"; sourceTree = "<group>"; };
|
||||
9E5D60021A5A5A3900689918 /* Roboto-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Regular.ttf"; sourceTree = "<group>"; };
|
||||
9E5D60031A5A5A3900689918 /* Roboto-Thin.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Thin.ttf"; sourceTree = "<group>"; };
|
||||
FB290CEE19B2C406004C83CF /* Limelight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Limelight.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ComputerScrollView.h; sourceTree = "<group>"; };
|
||||
FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ComputerScrollView.m; sourceTree = "<group>"; };
|
||||
FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppCollectionView.h; sourceTree = "<group>"; };
|
||||
FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppCollectionView.m; sourceTree = "<group>"; };
|
||||
FB290CEE19B2C406004C83CF /* Moonlight.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Moonlight.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FB290CF119B2C406004C83CF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
FB290CF319B2C406004C83CF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
FB290CF519B2C406004C83CF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
@@ -115,10 +128,6 @@
|
||||
FB290D0319B2C406004C83CF /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Limelight.xcdatamodel; sourceTree = "<group>"; };
|
||||
FB290D0819B2C406004C83CF /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
FB290D0E19B2C406004C83CF /* LimelightTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LimelightTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
FB290D1819B2C406004C83CF /* LimelightTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "LimelightTests-Info.plist"; sourceTree = "<group>"; };
|
||||
FB290D1A19B2C406004C83CF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
FB290D1C19B2C406004C83CF /* LimelightTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LimelightTests.m; sourceTree = "<group>"; };
|
||||
FB290DB619B2C870004C83CF /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
|
||||
FB290DB819B2C877004C83CF /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = usr/lib/libbz2.dylib; sourceTree = SDKROOT; };
|
||||
FB290DC319B2E98F004C83CF /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = usr/lib/libxml2.dylib; sourceTree = SDKROOT; };
|
||||
@@ -132,9 +141,15 @@
|
||||
FB4678FD1A565DAC00377732 /* WakeOnLanManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WakeOnLanManager.h; sourceTree = "<group>"; };
|
||||
FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WakeOnLanManager.m; sourceTree = "<group>"; };
|
||||
FB4679001A57048000377732 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
|
||||
FB4A23B61A9D3637004D2EF2 /* LoadingFrameViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoadingFrameViewController.h; sourceTree = "<group>"; };
|
||||
FB4A23B71A9D3637004D2EF2 /* LoadingFrameViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoadingFrameViewController.m; sourceTree = "<group>"; };
|
||||
FB53E1411BE5DC4400CD6ECE /* IdManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IdManager.h; sourceTree = "<group>"; };
|
||||
FB53E1421BE5DC4400CD6ECE /* IdManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IdManager.m; sourceTree = "<group>"; };
|
||||
FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.0-2.xcdatamodel"; sourceTree = "<group>"; };
|
||||
FB6549541A57907E001C8F39 /* DiscoveryWorker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiscoveryWorker.h; sourceTree = "<group>"; };
|
||||
FB6549551A57907E001C8F39 /* DiscoveryWorker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DiscoveryWorker.m; sourceTree = "<group>"; };
|
||||
FB6549621A60B4A9001C8F39 /* Limelight 0.3.1.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Limelight 0.3.1.xcdatamodel"; sourceTree = "<group>"; };
|
||||
FB6DDEF31AE739100096499A /* libmoonlight-common.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libmoonlight-common.a"; path = "limelight-common-c/build/Debug-iphoneos/libmoonlight-common.a"; sourceTree = "<group>"; };
|
||||
FB7E794319C8B71B00A15F68 /* libiconv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libiconv.dylib; path = usr/lib/libiconv.dylib; sourceTree = SDKROOT; };
|
||||
FB89460519F646E200339C8A /* CryptoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CryptoManager.h; sourceTree = "<group>"; };
|
||||
FB89460619F646E200339C8A /* CryptoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CryptoManager.m; sourceTree = "<group>"; };
|
||||
@@ -247,15 +262,30 @@
|
||||
FB8946E719F6AFB800339C8A /* opus_multistream.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = opus_multistream.h; sourceTree = "<group>"; };
|
||||
FB8946E819F6AFB800339C8A /* opus_types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = opus_types.h; sourceTree = "<group>"; };
|
||||
FB8946EA19F6AFB800339C8A /* libopus.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libopus.a; sourceTree = "<group>"; };
|
||||
FBD3494119FC9C04002D2A60 /* AppManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppManager.h; sourceTree = "<group>"; };
|
||||
FBD3494219FC9C04002D2A60 /* AppManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppManager.m; sourceTree = "<group>"; };
|
||||
FB9AFD261A7C84ED00872C98 /* HttpResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpResponse.h; sourceTree = "<group>"; };
|
||||
FB9AFD271A7C84ED00872C98 /* HttpResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HttpResponse.m; sourceTree = "<group>"; };
|
||||
FB9AFD301A7D867C00872C98 /* AppAssetRetriever.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAssetRetriever.h; sourceTree = "<group>"; };
|
||||
FB9AFD311A7D867C00872C98 /* AppAssetRetriever.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAssetRetriever.m; sourceTree = "<group>"; };
|
||||
FB9AFD351A7E02DB00872C98 /* HttpRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HttpRequest.h; sourceTree = "<group>"; };
|
||||
FB9AFD361A7E02DB00872C98 /* HttpRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HttpRequest.m; sourceTree = "<group>"; };
|
||||
FB9AFD381A7E05CE00872C98 /* ServerInfoResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ServerInfoResponse.h; sourceTree = "<group>"; };
|
||||
FB9AFD391A7E05CE00872C98 /* ServerInfoResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ServerInfoResponse.m; sourceTree = "<group>"; };
|
||||
FB9AFD3B1A7E111600872C98 /* AppAssetResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAssetResponse.h; sourceTree = "<group>"; };
|
||||
FB9AFD3C1A7E111600872C98 /* AppAssetResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAssetResponse.m; sourceTree = "<group>"; };
|
||||
FB9AFD3E1A7E127D00872C98 /* AppListResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppListResponse.h; sourceTree = "<group>"; };
|
||||
FB9AFD3F1A7E127D00872C98 /* AppListResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppListResponse.m; sourceTree = "<group>"; };
|
||||
FB9AFD411A7F0C6900872C98 /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Controller.h; sourceTree = "<group>"; };
|
||||
FB9AFD421A7F0C6900872C98 /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Controller.m; sourceTree = "<group>"; };
|
||||
FBB460391B50ACE400F3099C /* Moonlight v1.0.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.0.xcdatamodel"; sourceTree = "<group>"; };
|
||||
FBD1C8E01A8AD69E00C6703C /* Logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logger.h; sourceTree = "<group>"; };
|
||||
FBD1C8E11A8AD71400C6703C /* Logger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Logger.m; sourceTree = "<group>"; };
|
||||
FBD3494119FC9C04002D2A60 /* AppAssetManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppAssetManager.h; sourceTree = "<group>"; };
|
||||
FBD3494219FC9C04002D2A60 /* AppAssetManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppAssetManager.m; sourceTree = "<group>"; };
|
||||
FBD3494E19FF2174002D2A60 /* SettingsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SettingsViewController.h; sourceTree = "<group>"; };
|
||||
FBD3494F19FF2174002D2A60 /* SettingsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SettingsViewController.m; sourceTree = "<group>"; };
|
||||
FBD3495119FF36FB002D2A60 /* SWRevealViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SWRevealViewController.h; sourceTree = "<group>"; };
|
||||
FBD3495219FF36FB002D2A60 /* SWRevealViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SWRevealViewController.m; sourceTree = "<group>"; };
|
||||
FBD349571A003F05002D2A60 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
|
||||
FBD349591A004411002D2A60 /* Host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Host.h; path = Database/Host.h; sourceTree = "<group>"; };
|
||||
FBD3495A1A004411002D2A60 /* Host.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Host.m; path = Database/Host.m; sourceTree = "<group>"; };
|
||||
FBD3495C1A004412002D2A60 /* Settings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Settings.h; path = Database/Settings.h; sourceTree = "<group>"; };
|
||||
FBD3495D1A004412002D2A60 /* Settings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Settings.m; path = Database/Settings.m; sourceTree = "<group>"; };
|
||||
FBD349601A0089F6002D2A60 /* DataManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DataManager.h; path = Database/DataManager.h; sourceTree = "<group>"; };
|
||||
@@ -264,8 +294,12 @@
|
||||
FBDE86DF19F7A837001C18A8 /* UIComputerView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIComputerView.m; sourceTree = "<group>"; };
|
||||
FBDE86E419F82297001C18A8 /* UIAppView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UIAppView.h; sourceTree = "<group>"; };
|
||||
FBDE86E519F82297001C18A8 /* UIAppView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIAppView.m; sourceTree = "<group>"; };
|
||||
FBDE86E719F82315001C18A8 /* App.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = App.h; path = ../App.h; sourceTree = "<group>"; };
|
||||
FBDE86E819F82315001C18A8 /* App.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = App.m; path = ../App.m; sourceTree = "<group>"; };
|
||||
FBFCB32E1B50B29400089F8A /* Host+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "Host+CoreDataProperties.h"; path = "Database/Host+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
FBFCB32F1B50B29400089F8A /* Host.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Host.h; path = Database/Host.h; sourceTree = "<group>"; };
|
||||
FBFCB3301B50B29400089F8A /* Host.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Host.m; path = Database/Host.m; sourceTree = "<group>"; };
|
||||
FBFCB3321B50B29400089F8A /* App+CoreDataProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "App+CoreDataProperties.h"; path = "Database/App+CoreDataProperties.h"; sourceTree = "<group>"; };
|
||||
FBFCB3331B50B29400089F8A /* App.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = App.h; path = Database/App.h; sourceTree = "<group>"; };
|
||||
FBFCB3341B50B29400089F8A /* App.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = App.m; path = Database/App.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -273,7 +307,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
98A03B4D19F352EB00861ACA /* liblimelight-common.a in Frameworks */,
|
||||
987140041B04542F00AB57D5 /* libmoonlight-common.a in Frameworks */,
|
||||
FB8946ED19F6AFE800339C8A /* libopus.a in Frameworks */,
|
||||
FB8946EB19F6AFE100339C8A /* libcrypto.a in Frameworks */,
|
||||
FB8946EC19F6AFE400339C8A /* libssl.a in Frameworks */,
|
||||
@@ -289,27 +323,9 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
FB290D0B19B2C406004C83CF /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FB290D1319B2C406004C83CF /* CoreData.framework in Frameworks */,
|
||||
FB290D1219B2C406004C83CF /* UIKit.framework in Frameworks */,
|
||||
FB290D1119B2C406004C83CF /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
98A03B4619F3514B00861ACA /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
98A03B4A19F3514B00861ACA /* liblimelight-common.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E5D5FF61A5A5A3900689918 /* Font */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -332,12 +348,19 @@
|
||||
path = Roboto;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB1E430C1AE8B0F200AFF679 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB1E43101AE8B0F200AFF679 /* libmoonlight-common.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB290CE519B2C406004C83CF = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
98A03B4519F3514B00861ACA /* limelight-common.xcodeproj */,
|
||||
98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */,
|
||||
FB290CF919B2C406004C83CF /* Limelight */,
|
||||
FB290D1619B2C406004C83CF /* LimelightTests */,
|
||||
FB290CF019B2C406004C83CF /* Frameworks */,
|
||||
FB290CEF19B2C406004C83CF /* Products */,
|
||||
);
|
||||
@@ -346,8 +369,7 @@
|
||||
FB290CEF19B2C406004C83CF /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB290CEE19B2C406004C83CF /* Limelight.app */,
|
||||
FB290D0E19B2C406004C83CF /* LimelightTests.xctest */,
|
||||
FB290CEE19B2C406004C83CF /* Moonlight.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
@@ -355,6 +377,7 @@
|
||||
FB290CF019B2C406004C83CF /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB6DDEF31AE739100096499A /* libmoonlight-common.a */,
|
||||
FB4679001A57048000377732 /* CoreFoundation.framework */,
|
||||
FBD349571A003F05002D2A60 /* libsqlite3.dylib */,
|
||||
FB89468F19F6AFB800339C8A /* libs */,
|
||||
@@ -393,6 +416,10 @@
|
||||
FB89463719F6473800339C8A /* Launch Screen.xib */,
|
||||
FB290D0819B2C406004C83CF /* Images.xcassets */,
|
||||
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */,
|
||||
FB1D59951BBCCB6400F482CA /* ComputerScrollView.h */,
|
||||
FB1D59961BBCCB6400F482CA /* ComputerScrollView.m */,
|
||||
FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */,
|
||||
FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */,
|
||||
);
|
||||
path = Limelight;
|
||||
sourceTree = "<group>";
|
||||
@@ -408,24 +435,6 @@
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB290D1619B2C406004C83CF /* LimelightTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB290D1C19B2C406004C83CF /* LimelightTests.m */,
|
||||
FB290D1719B2C406004C83CF /* Supporting Files */,
|
||||
);
|
||||
path = LimelightTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB290D1719B2C406004C83CF /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB290D1819B2C406004C83CF /* LimelightTests-Info.plist */,
|
||||
FB290D1919B2C406004C83CF /* InfoPlist.strings */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB89460419F646E200339C8A /* Crypto */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -433,6 +442,8 @@
|
||||
FB89460619F646E200339C8A /* CryptoManager.m */,
|
||||
FB89460719F646E200339C8A /* mkcert.c */,
|
||||
FB89460819F646E200339C8A /* mkcert.h */,
|
||||
FB53E1411BE5DC4400CD6ECE /* IdManager.h */,
|
||||
FB53E1421BE5DC4400CD6ECE /* IdManager.m */,
|
||||
);
|
||||
path = Crypto;
|
||||
sourceTree = "<group>";
|
||||
@@ -442,6 +453,8 @@
|
||||
children = (
|
||||
FB89460A19F646E200339C8A /* ControllerSupport.h */,
|
||||
FB89460B19F646E200339C8A /* ControllerSupport.m */,
|
||||
FB9AFD411A7F0C6900872C98 /* Controller.h */,
|
||||
FB9AFD421A7F0C6900872C98 /* Controller.m */,
|
||||
FB89460C19F646E200339C8A /* StreamView.h */,
|
||||
FB89460D19F646E200339C8A /* StreamView.m */,
|
||||
FB4678EB1A50C40900377732 /* OnScreenControls.h */,
|
||||
@@ -453,20 +466,13 @@
|
||||
FB89460E19F646E200339C8A /* Network */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB89460F19F646E200339C8A /* HttpManager.h */,
|
||||
FB89461019F646E200339C8A /* HttpManager.m */,
|
||||
FB89461119F646E200339C8A /* MDNSManager.h */,
|
||||
FB89461219F646E200339C8A /* MDNSManager.m */,
|
||||
FB9AFD2F1A7C979700872C98 /* Http */,
|
||||
FB9AFD341A7D877B00872C98 /* Discovery */,
|
||||
FB9AFD331A7D876F00872C98 /* AppList */,
|
||||
FB89461319F646E200339C8A /* PairManager.h */,
|
||||
FB89461419F646E200339C8A /* PairManager.m */,
|
||||
FBD3494119FC9C04002D2A60 /* AppManager.h */,
|
||||
FBD3494219FC9C04002D2A60 /* AppManager.m */,
|
||||
FB4678FD1A565DAC00377732 /* WakeOnLanManager.h */,
|
||||
FB4678FE1A565DAC00377732 /* WakeOnLanManager.m */,
|
||||
FB4678F81A55FFAD00377732 /* DiscoveryManager.h */,
|
||||
FB4678F91A55FFAD00377732 /* DiscoveryManager.m */,
|
||||
FB6549541A57907E001C8F39 /* DiscoveryWorker.h */,
|
||||
FB6549551A57907E001C8F39 /* DiscoveryWorker.m */,
|
||||
);
|
||||
path = Network;
|
||||
sourceTree = "<group>";
|
||||
@@ -489,10 +495,10 @@
|
||||
FB89461E19F646E200339C8A /* Utility */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FBDE86E719F82315001C18A8 /* App.h */,
|
||||
FBDE86E819F82315001C18A8 /* App.m */,
|
||||
FB89462119F646E200339C8A /* Utils.h */,
|
||||
FB89462219F646E200339C8A /* Utils.m */,
|
||||
FBD1C8E01A8AD69E00C6703C /* Logger.h */,
|
||||
FBD1C8E11A8AD71400C6703C /* Logger.m */,
|
||||
);
|
||||
path = Utility;
|
||||
sourceTree = "<group>";
|
||||
@@ -508,6 +514,8 @@
|
||||
FB89462719F646E200339C8A /* StreamFrameViewController.m */,
|
||||
FBD3494E19FF2174002D2A60 /* SettingsViewController.h */,
|
||||
FBD3494F19FF2174002D2A60 /* SettingsViewController.m */,
|
||||
FB4A23B61A9D3637004D2EF2 /* LoadingFrameViewController.h */,
|
||||
FB4A23B71A9D3637004D2EF2 /* LoadingFrameViewController.m */,
|
||||
);
|
||||
path = ViewControllers;
|
||||
sourceTree = "<group>";
|
||||
@@ -673,15 +681,68 @@
|
||||
path = lib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB9AFD2F1A7C979700872C98 /* Http */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB89460F19F646E200339C8A /* HttpManager.h */,
|
||||
FB89461019F646E200339C8A /* HttpManager.m */,
|
||||
FB9AFD351A7E02DB00872C98 /* HttpRequest.h */,
|
||||
FB9AFD361A7E02DB00872C98 /* HttpRequest.m */,
|
||||
FB9AFD261A7C84ED00872C98 /* HttpResponse.h */,
|
||||
FB9AFD271A7C84ED00872C98 /* HttpResponse.m */,
|
||||
FB9AFD381A7E05CE00872C98 /* ServerInfoResponse.h */,
|
||||
FB9AFD391A7E05CE00872C98 /* ServerInfoResponse.m */,
|
||||
FB9AFD3B1A7E111600872C98 /* AppAssetResponse.h */,
|
||||
FB9AFD3C1A7E111600872C98 /* AppAssetResponse.m */,
|
||||
FB9AFD3E1A7E127D00872C98 /* AppListResponse.h */,
|
||||
FB9AFD3F1A7E127D00872C98 /* AppListResponse.m */,
|
||||
);
|
||||
name = Http;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB9AFD331A7D876F00872C98 /* AppList */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FBD3494119FC9C04002D2A60 /* AppAssetManager.h */,
|
||||
FBD3494219FC9C04002D2A60 /* AppAssetManager.m */,
|
||||
FB9AFD301A7D867C00872C98 /* AppAssetRetriever.h */,
|
||||
FB9AFD311A7D867C00872C98 /* AppAssetRetriever.m */,
|
||||
);
|
||||
name = AppList;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB9AFD341A7D877B00872C98 /* Discovery */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FB4678F81A55FFAD00377732 /* DiscoveryManager.h */,
|
||||
FB4678F91A55FFAD00377732 /* DiscoveryManager.m */,
|
||||
FB6549541A57907E001C8F39 /* DiscoveryWorker.h */,
|
||||
FB6549551A57907E001C8F39 /* DiscoveryWorker.m */,
|
||||
FB89461119F646E200339C8A /* MDNSManager.h */,
|
||||
FB89461219F646E200339C8A /* MDNSManager.m */,
|
||||
);
|
||||
name = Discovery;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FBD3495F1A004453002D2A60 /* Database */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FBFCB3321B50B29400089F8A /* App+CoreDataProperties.h */,
|
||||
FBFCB3331B50B29400089F8A /* App.h */,
|
||||
FBFCB3341B50B29400089F8A /* App.m */,
|
||||
FBFCB32E1B50B29400089F8A /* Host+CoreDataProperties.h */,
|
||||
FBFCB32F1B50B29400089F8A /* Host.h */,
|
||||
FBFCB3301B50B29400089F8A /* Host.m */,
|
||||
FBD3495C1A004412002D2A60 /* Settings.h */,
|
||||
FBD3495D1A004412002D2A60 /* Settings.m */,
|
||||
FBD349591A004411002D2A60 /* Host.h */,
|
||||
FBD3495A1A004411002D2A60 /* Host.m */,
|
||||
FBD349601A0089F6002D2A60 /* DataManager.h */,
|
||||
FBD349611A0089F6002D2A60 /* DataManager.m */,
|
||||
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */,
|
||||
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */,
|
||||
98D5856B1C0EA79600F6CC00 /* TemporaryHost.h */,
|
||||
98D5856C1C0EA79600F6CC00 /* TemporaryHost.m */,
|
||||
98D5856E1C0ED0E800F6CC00 /* TemporarySettings.h */,
|
||||
98D5856F1C0ED0E800F6CC00 /* TemporarySettings.m */,
|
||||
);
|
||||
name = Database;
|
||||
sourceTree = "<group>";
|
||||
@@ -689,9 +750,9 @@
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
FB290CED19B2C406004C83CF /* Limelight */ = {
|
||||
FB290CED19B2C406004C83CF /* Moonlight */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = FB290D2019B2C406004C83CF /* Build configuration list for PBXNativeTarget "Limelight" */;
|
||||
buildConfigurationList = FB290D2019B2C406004C83CF /* Build configuration list for PBXNativeTarget "Moonlight" */;
|
||||
buildPhases = (
|
||||
FB290CEA19B2C406004C83CF /* Sources */,
|
||||
FB290CEB19B2C406004C83CF /* Frameworks */,
|
||||
@@ -700,50 +761,29 @@
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
98A03B4C19F3517100861ACA /* PBXTargetDependency */,
|
||||
987140021B04541A00AB57D5 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Limelight;
|
||||
name = Moonlight;
|
||||
productName = Limelight;
|
||||
productReference = FB290CEE19B2C406004C83CF /* Limelight.app */;
|
||||
productReference = FB290CEE19B2C406004C83CF /* Moonlight.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
FB290D0D19B2C406004C83CF /* LimelightTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = FB290D2319B2C406004C83CF /* Build configuration list for PBXNativeTarget "LimelightTests" */;
|
||||
buildPhases = (
|
||||
FB290D0A19B2C406004C83CF /* Sources */,
|
||||
FB290D0B19B2C406004C83CF /* Frameworks */,
|
||||
FB290D0C19B2C406004C83CF /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
FB290D1519B2C406004C83CF /* PBXTargetDependency */,
|
||||
);
|
||||
name = LimelightTests;
|
||||
productName = LimelightTests;
|
||||
productReference = FB290D0E19B2C406004C83CF /* LimelightTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
FB290CE619B2C406004C83CF /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
ORGANIZATIONNAME = "Limelight Stream";
|
||||
LastUpgradeCheck = 0700;
|
||||
ORGANIZATIONNAME = "Moonlight Stream";
|
||||
TargetAttributes = {
|
||||
FB290CED19B2C406004C83CF = {
|
||||
DevelopmentTeam = DM46QST4M7;
|
||||
};
|
||||
FB290D0D19B2C406004C83CF = {
|
||||
TestTargetID = FB290CED19B2C406004C83CF;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = FB290CE919B2C406004C83CF /* Build configuration list for PBXProject "Limelight" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
buildConfigurationList = FB290CE919B2C406004C83CF /* Build configuration list for PBXProject "Moonlight" */;
|
||||
compatibilityVersion = "Xcode 6.3";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
@@ -754,24 +794,23 @@
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = 98A03B4619F3514B00861ACA /* Products */;
|
||||
ProjectRef = 98A03B4519F3514B00861ACA /* limelight-common.xcodeproj */;
|
||||
ProductGroup = FB1E430C1AE8B0F200AFF679 /* Products */;
|
||||
ProjectRef = 98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */;
|
||||
},
|
||||
);
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
FB290CED19B2C406004C83CF /* Limelight */,
|
||||
FB290D0D19B2C406004C83CF /* LimelightTests */,
|
||||
FB290CED19B2C406004C83CF /* Moonlight */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
98A03B4A19F3514B00861ACA /* liblimelight-common.a */ = {
|
||||
FB1E43101AE8B0F200AFF679 /* libmoonlight-common.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = "liblimelight-common.a";
|
||||
remoteRef = 98A03B4919F3514B00861ACA /* PBXContainerItemProxy */;
|
||||
path = "libmoonlight-common.a";
|
||||
remoteRef = FB1E430F1AE8B0F200AFF679 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
@@ -796,14 +835,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
FB290D0C19B2C406004C83CF /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FB290D1B19B2C406004C83CF /* InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -815,55 +846,57 @@
|
||||
FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */,
|
||||
FBD3495E1A004412002D2A60 /* Settings.m in Sources */,
|
||||
FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */,
|
||||
FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */,
|
||||
FB89463419F646E200339C8A /* Utils.m in Sources */,
|
||||
FBDE86E619F82297001C18A8 /* UIAppView.m in Sources */,
|
||||
FB89462F19F646E200339C8A /* Connection.m in Sources */,
|
||||
FB9AFD321A7D867C00872C98 /* AppAssetRetriever.m in Sources */,
|
||||
FB4678FF1A565DAC00377732 /* WakeOnLanManager.m in Sources */,
|
||||
FB53E1431BE5DC4400CD6ECE /* IdManager.m in Sources */,
|
||||
FB89462919F646E200339C8A /* mkcert.c in Sources */,
|
||||
FB9AFD281A7C84ED00872C98 /* HttpResponse.m in Sources */,
|
||||
FBDE86E019F7A837001C18A8 /* UIComputerView.m in Sources */,
|
||||
FBDE86E919F82315001C18A8 /* App.m in Sources */,
|
||||
FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */,
|
||||
FBFCB3311B50B29400089F8A /* Host.m in Sources */,
|
||||
FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */,
|
||||
FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */,
|
||||
FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */,
|
||||
FB89462C19F646E200339C8A /* HttpManager.m in Sources */,
|
||||
FB89462D19F646E200339C8A /* MDNSManager.m in Sources */,
|
||||
FB89462B19F646E200339C8A /* StreamView.m in Sources */,
|
||||
FB4678FA1A55FFAD00377732 /* DiscoveryManager.m in Sources */,
|
||||
FBD3495B1A004411002D2A60 /* Host.m in Sources */,
|
||||
98D5856D1C0EA79600F6CC00 /* TemporaryHost.m in Sources */,
|
||||
FB89463519F646E200339C8A /* MainFrameViewController.m in Sources */,
|
||||
FBD1C8E21A8AD71400C6703C /* Logger.m in Sources */,
|
||||
FB1D599A1BBCCD7E00F482CA /* AppCollectionView.m in Sources */,
|
||||
FB89463619F646E200339C8A /* StreamFrameViewController.m in Sources */,
|
||||
9832D1361BBCD5C50036EF48 /* TemporaryApp.m in Sources */,
|
||||
98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */,
|
||||
FB89462819F646E200339C8A /* CryptoManager.m in Sources */,
|
||||
FB89462E19F646E200339C8A /* PairManager.m in Sources */,
|
||||
FB9AFD371A7E02DB00872C98 /* HttpRequest.m in Sources */,
|
||||
FB4678ED1A50C40900377732 /* OnScreenControls.m in Sources */,
|
||||
FB290D0019B2C406004C83CF /* main.m in Sources */,
|
||||
FBD3494319FC9C04002D2A60 /* AppManager.m in Sources */,
|
||||
FB9AFD431A7F0C6900872C98 /* Controller.m in Sources */,
|
||||
FBD3494319FC9C04002D2A60 /* AppAssetManager.m in Sources */,
|
||||
FB6549561A57907E001C8F39 /* DiscoveryWorker.m in Sources */,
|
||||
FB89462A19F646E200339C8A /* ControllerSupport.m in Sources */,
|
||||
FB9AFD3D1A7E111600872C98 /* AppAssetResponse.m in Sources */,
|
||||
FBD349621A0089F6002D2A60 /* DataManager.m in Sources */,
|
||||
FBFCB3351B50B29400089F8A /* App.m in Sources */,
|
||||
FB4A23B81A9D3637004D2EF2 /* LoadingFrameViewController.m in Sources */,
|
||||
FB9AFD3A1A7E05CE00872C98 /* ServerInfoResponse.m in Sources */,
|
||||
FB89463119F646E200339C8A /* StreamManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
FB290D0A19B2C406004C83CF /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
FB290D1D19B2C406004C83CF /* LimelightTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
98A03B4C19F3517100861ACA /* PBXTargetDependency */ = {
|
||||
987140021B04541A00AB57D5 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
name = "limelight-common";
|
||||
targetProxy = 98A03B4B19F3517100861ACA /* PBXContainerItemProxy */;
|
||||
};
|
||||
FB290D1519B2C406004C83CF /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = FB290CED19B2C406004C83CF /* Limelight */;
|
||||
targetProxy = FB290D1419B2C406004C83CF /* PBXContainerItemProxy */;
|
||||
name = "moonlight-common";
|
||||
targetProxy = 987140011B04541A00AB57D5 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
@@ -876,14 +909,6 @@
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB290D1919B2C406004C83CF /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
FB290D1A19B2C406004C83CF /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
@@ -906,6 +931,7 @@
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
@@ -968,6 +994,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
@@ -983,6 +1010,7 @@
|
||||
"$(PROJECT_DIR)/libs/opus/lib",
|
||||
"$(PROJECT_DIR)/libs/openssl/lib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.moonlight-stream.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
SKIP_INSTALL = NO;
|
||||
@@ -994,8 +1022,9 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Distribution";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
@@ -1011,6 +1040,7 @@
|
||||
"$(PROJECT_DIR)/libs/opus/lib",
|
||||
"$(PROJECT_DIR)/libs/openssl/lib",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.moonlight-stream.$(PRODUCT_NAME:rfc1034identifier)";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
SKIP_INSTALL = NO;
|
||||
@@ -1018,50 +1048,10 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
FB290D2419B2C406004C83CF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Limelight.app/Limelight";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "LimelightTests/LimelightTests-Info.plist";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
FB290D2519B2C406004C83CF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/Limelight.app/Limelight";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "Limelight/Limelight-Prefix.pch";
|
||||
INFOPLIST_FILE = "LimelightTests/LimelightTests-Info.plist";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
FB290CE919B2C406004C83CF /* Build configuration list for PBXProject "Limelight" */ = {
|
||||
FB290CE919B2C406004C83CF /* Build configuration list for PBXProject "Moonlight" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
FB290D1E19B2C406004C83CF /* Debug */,
|
||||
@@ -1070,7 +1060,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
FB290D2019B2C406004C83CF /* Build configuration list for PBXNativeTarget "Limelight" */ = {
|
||||
FB290D2019B2C406004C83CF /* Build configuration list for PBXNativeTarget "Moonlight" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
FB290D2119B2C406004C83CF /* Debug */,
|
||||
@@ -1079,26 +1069,19 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
FB290D2319B2C406004C83CF /* Build configuration list for PBXNativeTarget "LimelightTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
FB290D2419B2C406004C83CF /* Debug */,
|
||||
FB290D2519B2C406004C83CF /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */,
|
||||
FBB460391B50ACE400F3099C /* Moonlight v1.0.xcdatamodel */,
|
||||
FB6549621A60B4A9001C8F39 /* Limelight 0.3.1.xcdatamodel */,
|
||||
FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */,
|
||||
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */,
|
||||
);
|
||||
currentVersion = FB6549621A60B4A9001C8F39 /* Limelight 0.3.1.xcdatamodel */;
|
||||
currentVersion = FB53E1441BE5DCBC00CD6ECE /* Moonlight v1.0-2.xcdatamodel */;
|
||||
path = Limelight.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
@@ -2,6 +2,6 @@
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Limelight.xcodeproj">
|
||||
location = "self:Moonlight.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>AD7C6582-F39A-441A-A7FD-9EBF143920BA</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>Moonlight</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</key>
|
||||
<string>github.com:limelight-stream/limelight-ios.git</string>
|
||||
<key>EB99D631D5149240E91F8D168F0F298D7A5BAA89</key>
|
||||
<string>github.com:limelight-stream/limelight-common-c.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>Moonlight.xcodeproj</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</key>
|
||||
<string>../..</string>
|
||||
<key>EB99D631D5149240E91F8D168F0F298D7A5BAA89</key>
|
||||
<string>../../limelight-common-c</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>github.com:limelight-stream/limelight-ios.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>EB99D631D5149240E91F8D168F0F298D7A5BAA89</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>limelight-common-c</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>moonlight-ios</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>6524503E-ED62-4E8E-8A67-B6DFB5A532FD</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>project</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</key>
|
||||
<string>github.com:limelight-stream/limelight-ios.git</string>
|
||||
<key>EB99D631D5149240E91F8D168F0F298D7A5BAA89</key>
|
||||
<string>github.com:limelight-stream/limelight-common-c.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>Moonlight.xcodeproj/project.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</key>
|
||||
<string>../..</string>
|
||||
<key>EB99D631D5149240E91F8D168F0F298D7A5BAA89</key>
|
||||
<string>../..limelight-common-c/</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>github.com:limelight-stream/limelight-ios.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>EB99D631D5149240E91F8D168F0F298D7A5BAA89</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>limelight-common-c</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>A4B0849259F9566B0CBA84A6D3D4A0F895C03123</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>limelight-ios</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -1,13 +1,14 @@
|
||||
#Limelight iOS
|
||||
#Moonlight iOS
|
||||
|
||||
[Limelight](https://github.com/limelight-stream) is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for iOS. Limelight iOS will allow you to stream your full collection of Steam games from
|
||||
#### Now available on the [App Store](https://itunes.apple.com/us/app/moonlight-game-streaming/id1000551566?mt=8)
|
||||
|
||||
[Moonlight](http://moonlight-stream.com) 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
|
||||
your powerful desktop computer to your iOS Device.
|
||||
|
||||
This is actively being developed and should be available on the AppStore soon.
|
||||
There are also versions for [Android](https://github.com/moonlight-stream/moonlight-android) and [PC](https://github.com/moonlight-stream/moonlight-pc).
|
||||
|
||||
We understand that Limelight users are eager for the opportunity to stream their Steam collection to their iOS devices. Limelight for iOS is in the testing stage and preliminary versions are available for beta testers.
|
||||
|
||||
In the meantime, check out the existing versions for [Android](https://github.com/limelight-stream/limelight-android) and [PC](https://github.com/limelight-stream/limelight-pc). Also, a client for [Windows and Windows Phone](https://github.com/limelight-stream/limelight-windows) is in the works and actively being developed.
|
||||
|
||||
Please be patient as we work to bring Limelight to more platforms.
|
||||
##### Apple TV Note:
|
||||
We current cannot write a port for Apple TV due to a limitation in the tvOS SDK that prevents Moonlight from using the APIs that would be necessary for it to work. Thank you for your interest - you can track this issue [here](https://github.com/moonlight-stream/moonlight-ios/issues/184).
|
||||
|
||||
##### Questions?
|
||||
Check out our [wiki](https://github.com/moonlight-stream/moonlight-android/wiki).
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C81f" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" initialViewController="EVd-wq-ego">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8191" systemVersion="15B38b" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" initialViewController="EVd-wq-ego">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
|
||||
</dependencies>
|
||||
<customFonts key="customFonts">
|
||||
<mutableArray key="Roboto-Regular.ttf">
|
||||
@@ -13,9 +14,11 @@
|
||||
<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">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1024" height="704"/>
|
||||
<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="64" width="1024" height="704"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<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"/>
|
||||
@@ -24,14 +27,16 @@
|
||||
<inset key="sectionInset" minX="20" minY="20" maxX="20" maxY="20"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AppCell" id="fv6-NS-qsK">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<collectionViewCell clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AppCell" id="fv6-NS-qsK">
|
||||
<rect key="frame" x="20" y="40" width="150" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="150" height="200"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<animations/>
|
||||
</collectionViewCell>
|
||||
</cells>
|
||||
<connections>
|
||||
@@ -42,14 +47,15 @@
|
||||
<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="101" y="236" width="109" height="46"/>
|
||||
<rect key="frame" x="20" y="-1" width="109" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" name="Roboto-Regular" family="Roboto" pointSize="16"/>
|
||||
<state key="normal" title="Limelight" image="Logo">
|
||||
<color key="titleColor" red="0.3921568627" green="0.91372549020000005" blue="0.17254901959999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<state key="normal" title="Moonlight" image="Logo">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
</button>
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" title="No Host Selected" id="KDy-JC-2sU">
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
@@ -77,18 +83,23 @@
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Bitrate: 10 Mbps" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="lMt-4H-fkV">
|
||||
<rect key="frame" x="16" y="39" width="151" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="10" minValue="1" maxValue="50" id="JAY-nj-UNz">
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="20" minValue="1" maxValue="100" id="JAY-nj-UNz">
|
||||
<rect key="frame" x="14" y="68" width="213" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="tintColor" red="0.3921568627" green="0.91372549020000005" blue="0.17254901959999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<animations/>
|
||||
<color key="tintColor" red="0.72941176470000002" green="0.69411764710000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</slider>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Framerate" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Aiv-r8-9k2">
|
||||
<rect key="frame" x="16" y="106" width="79" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -96,6 +107,8 @@
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="pYZ-GR-EwO">
|
||||
<rect key="frame" x="16" y="171" width="82" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -103,25 +116,18 @@
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="ckc-Dm-8ex">
|
||||
<rect key="frame" x="16" y="200" width="209" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<segments>
|
||||
<segment title="720p"/>
|
||||
<segment title="1080p"/>
|
||||
<segment title="Native"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.3921568627" green="0.91372549020000005" blue="0.17254901959999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</segmentedControl>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="lGK-vl-pdw">
|
||||
<rect key="frame" x="16" y="135" width="209" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<segments>
|
||||
<segment title="30 Hz"/>
|
||||
<segment title="60 Hz"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.3921568627" green="0.91372549020000005" blue="0.17254901959999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="tintColor" red="0.72941176470000002" green="0.69411764710000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</segmentedControl>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="On-Screen Controls" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="LGV-5y-piG">
|
||||
<rect key="frame" x="16" y="236" width="153" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<color key="tintColor" red="0.81533776120000001" green="0.85979419950000002" blue="0.91372549020000005" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -129,15 +135,27 @@
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="qSU-wh-tqA">
|
||||
<rect key="frame" x="16" y="265" width="209" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<segments>
|
||||
<segment title="Off"/>
|
||||
<segment title="Auto"/>
|
||||
<segment title="Simple"/>
|
||||
<segment title="Full"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.3803921569" green="0.92156862750000001" blue="0.25882352939999997" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="tintColor" red="0.72941176470000002" green="0.69411764710000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</segmentedControl>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="lGK-vl-pdw">
|
||||
<rect key="frame" x="16" y="135" width="209" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<segments>
|
||||
<segment title="30 Hz"/>
|
||||
<segment title="60 Hz"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.72941176470000002" green="0.69411764710000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="viewFlipsideBackgroundColor"/>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
@@ -160,6 +178,7 @@
|
||||
<view key="view" contentMode="scaleToFill" id="hGs-BR-t4b">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
@@ -180,6 +199,8 @@
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" translucent="NO" id="RUe-14-4Ya">
|
||||
<rect key="frame" x="0.0" y="0.0" width="768" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
<color key="barTintColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="wb7-af-jn8" kind="relationship" relationship="rootViewController" id="nkY-JG-Wki"/>
|
||||
@@ -200,16 +221,19 @@
|
||||
<activityIndicatorView opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" id="iOs-1X-mSU">
|
||||
<rect key="frame" x="502" y="374" width="20" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
</activityIndicatorView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="dDs-kT-po6">
|
||||
<rect key="frame" x="491" y="402" width="42" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="iUf-9X-GeA"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
@@ -226,9 +250,35 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1946" y="514"/>
|
||||
</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">
|
||||
<view key="view" contentMode="scaleToFill" id="le1-tU-NEp">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1024" height="768"/>
|
||||
<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">
|
||||
<rect key="frame" x="494" y="365" width="37" height="37"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.5" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<connections>
|
||||
<outlet property="loadingSpinner" destination="N0D-dj-EuF" id="xEQ-nx-SOt"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="lJG-tq-Jrs" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1946" y="1425"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Logo" width="36" height="36"/>
|
||||
<image name="Logo" width="32" height="32"/>
|
||||
</resources>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar" statusBarStyle="lightContent"/>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C81f" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="DL0-L5-LOv">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8191" systemVersion="15B38b" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="DL0-L5-LOv">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
|
||||
</dependencies>
|
||||
<customFonts key="customFonts">
|
||||
<mutableArray key="Roboto-Regular.ttf">
|
||||
@@ -13,9 +14,11 @@
|
||||
<scene sceneID="emz-kO-L7q">
|
||||
<objects>
|
||||
<viewController id="dgh-JZ-Q7z" customClass="MainFrameViewController" sceneMemberID="viewController">
|
||||
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" indicatorStyle="black" dataMode="prototypes" id="Rtu-AT-Alw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="256"/>
|
||||
<collectionView key="view" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" id="Rtu-AT-Alw" customClass="AppCollectionView">
|
||||
<rect key="frame" x="0.0" y="64" width="568" height="256"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<inset key="contentInset" minX="40" minY="20" maxX="40" maxY="20"/>
|
||||
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="20" minimumInteritemSpacing="20" id="YcZ-cR-3tK">
|
||||
<size key="itemSize" width="90" height="120"/>
|
||||
@@ -24,14 +27,16 @@
|
||||
<inset key="sectionInset" minX="20" minY="20" maxX="20" maxY="20"/>
|
||||
</collectionViewFlowLayout>
|
||||
<cells>
|
||||
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AppCell" id="Uqv-Di-fzX">
|
||||
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
|
||||
<collectionViewCell clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="AppCell" id="Uqv-Di-fzX">
|
||||
<rect key="frame" x="20" y="40" width="90" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
|
||||
<rect key="frame" x="0.0" y="0.0" width="90" height="120"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<animations/>
|
||||
</collectionViewCell>
|
||||
</cells>
|
||||
<connections>
|
||||
@@ -43,17 +48,18 @@
|
||||
<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="0.0" y="0.0" width="109" height="46"/>
|
||||
<rect key="frame" x="20" y="-1" width="109" height="46"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" name="Roboto-Regular" family="Roboto" pointSize="16"/>
|
||||
<state key="normal" title="Limelight" image="Logo">
|
||||
<color key="titleColor" red="0.3921568627" green="0.91372549020000005" blue="0.17254901959999999" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<state key="normal" title="Moonlight" image="Logo">
|
||||
<color key="titleColor" red="0.96078431372549022" green="0.98039215686274506" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
</button>
|
||||
</barButtonItem>
|
||||
<barButtonItem key="rightBarButtonItem" title="No Host Selected" id="L1H-n4-HB7">
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="tintColor" red="0.96078431372549022" green="0.98039215686274506" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
@@ -74,6 +80,7 @@
|
||||
<view key="view" contentMode="scaleToFill" id="Usg-e0-g4a">
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="320"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
@@ -94,71 +101,79 @@
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="320"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="10" minValue="1" maxValue="50" id="3nn-MI-9Xu">
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="20" minValue="1" maxValue="100" id="3nn-MI-9Xu">
|
||||
<rect key="frame" x="14" y="49" width="204" height="31"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="tintColor" red="0.39215686274509803" green="0.9137254901960784" blue="0.17254901960784313" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<animations/>
|
||||
<color key="tintColor" red="0.72941176470588232" green="0.69411764705882351" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</slider>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Bitrate: 10 Mbps" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="SBv-Wn-LB7">
|
||||
<rect key="frame" x="16" y="20" width="151" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" red="0.96078431372549022" green="0.98039215686274506" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="1" id="dLF-qJ-2nY">
|
||||
<rect key="frame" x="16" y="116" width="200" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<segments>
|
||||
<segment title="30 Hz"/>
|
||||
<segment title="60 Hz"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.39215686274509803" green="0.9137254901960784" blue="0.17254901960784313" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="tintColor" red="0.72941176470588232" green="0.69411764705882351" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</segmentedControl>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Framerate" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Kkv-DF-MAl">
|
||||
<rect key="frame" x="16" y="87" width="79" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" red="0.96078431372549022" green="0.98039215686274506" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Resolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8zy-ri-Dqc">
|
||||
<rect key="frame" x="16" y="152" width="82" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" red="0.96078431372549022" green="0.98039215686274506" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" id="PCM-t4-Sha">
|
||||
<rect key="frame" x="16" y="181" width="200" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<segments>
|
||||
<segment title="720p"/>
|
||||
<segment title="1080p"/>
|
||||
<segment title="Native"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.39215686274509803" green="0.9137254901960784" blue="0.17254901960784313" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="tintColor" red="0.72941176470588232" green="0.69411764705882351" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</segmentedControl>
|
||||
<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">
|
||||
<rect key="frame" x="16" y="217" width="153" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="textColor" red="0.96078431372549022" green="0.98039215686274506" blue="1" 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">
|
||||
<rect key="frame" x="16" y="246" width="200" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<segments>
|
||||
<segment title="Off"/>
|
||||
<segment title="Auto"/>
|
||||
<segment title="Simple"/>
|
||||
<segment title="Full"/>
|
||||
</segments>
|
||||
<color key="tintColor" red="0.38039215686274508" green="0.92156862745098034" blue="0.25882352941176467" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<color key="tintColor" red="0.72941176470588232" green="0.69411764705882351" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</segmentedControl>
|
||||
</subviews>
|
||||
<color key="backgroundColor" cocoaTouchSystemColor="viewFlipsideBackgroundColor"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" red="0.12156862745098039" green="0.12941176470588237" blue="0.14117647058823529" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<connections>
|
||||
@@ -181,6 +196,8 @@
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" translucent="NO" id="0ZA-Ec-QgD">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<animations/>
|
||||
<color key="barTintColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<textAttributes key="titleTextAttributes">
|
||||
<offsetWrapper key="textShadowOffset" horizontal="0.0" vertical="0.0"/>
|
||||
</textAttributes>
|
||||
@@ -204,6 +221,7 @@
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Stage" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="2HK-Z5-4Ch">
|
||||
<rect key="frame" x="262" y="149" width="45" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
@@ -211,9 +229,11 @@
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="white" id="0vm-Iv-K4b">
|
||||
<rect key="frame" x="274" y="121" width="20" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<animations/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="8uX-4T-BiC"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
@@ -229,13 +249,33 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2092" y="796"/>
|
||||
</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">
|
||||
<view key="view" opaque="NO" clearsContextBeforeDrawing="NO" contentMode="center" id="nSw-dc-mi4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="568" height="320"/>
|
||||
<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="141" width="37" height="37"/>
|
||||
<animations/>
|
||||
</activityIndicatorView>
|
||||
</subviews>
|
||||
<animations/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.5" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<simulatedOrientationMetrics key="simulatedOrientationMetrics" orientation="landscapeRight"/>
|
||||
<connections>
|
||||
<outlet property="loadingSpinner" destination="oNu-Gu-QeM" id="5IK-qn-mIZ"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="GaV-PK-JSI" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="2104" y="1266"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="Logo" width="36" height="36"/>
|
||||
<image name="Logo" width="32" height="32"/>
|
||||
</resources>
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
|
||||
@@ -616,7 +616,10 @@ OPUS_EXPORT void opus_pcm_soft_clip(float *pcm, int frame_size, int channels, fl
|
||||
* merged. Splitting valid Opus packets is always guaranteed to succeed,
|
||||
* whereas merging valid packets only succeeds if all frames have the same
|
||||
* mode, bandwidth, and frame size, and when the total duration of the merged
|
||||
* packet is no more than 120 ms.
|
||||
* packet is no more than 120 ms. The 120 ms limit comes from the
|
||||
* specification and limits decoder memory requirements at a point where
|
||||
* framing overhead becomes negligible.
|
||||
*
|
||||
* The repacketizer currently only operates on elementary Opus
|
||||
* streams. It will not manipualte multistream packets successfully, except in
|
||||
* the degenerate case where they consist of data from a single stream.
|
||||
|
||||
@@ -713,6 +713,10 @@ extern "C" {
|
||||
OPUS_EXPORT const char *opus_strerror(int error);
|
||||
|
||||
/** Gets the libopus version string.
|
||||
*
|
||||
* Applications may look for the substring "-fixed" in the version string to
|
||||
* determine whether they have a fixed-point or floating-point build at
|
||||
* runtime.
|
||||
*
|
||||
* @returns Version string
|
||||
*/
|
||||
|
||||