Port for macOS (#311)

* merged moonlight-mac with moonlight-ios

* reverted to the original project.pbxproj

* cleaned up the code, fixed lots of unnecessary code duplications

* multicontroller support (not tested)

* new class that can be used for further modularization of the MainFrameViewController
This commit is contained in:
Felix Kratz
2018-03-27 08:50:40 +02:00
committed by Cameron Gutman
parent 1c86c4485d
commit 6cc165b589
73 changed files with 5116 additions and 239 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,68 @@
{
"images" : [
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "16x icon.png",
"scale" : "1x"
},
{
"size" : "16x16",
"idiom" : "mac",
"filename" : "32x icon-1.png",
"scale" : "2x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "32x icon.png",
"scale" : "1x"
},
{
"size" : "32x32",
"idiom" : "mac",
"filename" : "64x icon.png",
"scale" : "2x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "128x icon.png",
"scale" : "1x"
},
{
"size" : "128x128",
"idiom" : "mac",
"filename" : "256x icon-1.png",
"scale" : "2x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "256x icon.png",
"scale" : "1x"
},
{
"size" : "256x256",
"idiom" : "mac",
"filename" : "512x icon-1.png",
"scale" : "2x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "512x icon.png",
"scale" : "1x"
},
{
"size" : "512x512",
"idiom" : "mac",
"filename" : "1024x icon-1.png",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,37 @@
<?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>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2018 Felix. All rights reserved.</string>
<key>NSMainStoryboardFile</key>
<string>Mac</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@@ -0,0 +1,17 @@
//
// Control.h
// Moonlight macOS
//
// Created by Felix Kratz on 15.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#ifndef Control_h
#define Control_h
#include <stdio.h>
#import "ControllerSupport.h"
extern void initGamepad(ControllerSupport* controllerSupport);
#endif /* Control_h */

View File

@@ -0,0 +1,218 @@
//
// Control.m
// Moonlight macOS
//
// Created by Felix Kratz on 15.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#include "Gamepad.h"
#include "Control.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "Limelight.h"
#import "Moonlight-Swift.h"
@class Controller;
#ifdef _MSC_VER
#define snprintf _snprintf
#endif
Controller* _controller;
ControllerSupport* _controllerSupport;
NSMutableDictionary* _controllers;
typedef enum {
SELECT,
L3,
R3,
START,
UP,
RIGHT,
DOWN,
LEFT,
LB = 10,
RB,
Y,
B,
A,
X,
} ControllerKeys;
typedef enum {
LEFT_X,
LEFT_Y,
RIGHT_X,
RIGHT_Y,
LT = 14,
RT,
} ControllerAxis;
void onButtonDown(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) {
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
switch (buttonID) {
case SELECT:
[_controllerSupport setButtonFlag:_controller flags:BACK_FLAG];
break;
case L3:
[_controllerSupport setButtonFlag:_controller flags:LS_CLK_FLAG];
break;
case R3:
[_controllerSupport setButtonFlag:_controller flags:RS_CLK_FLAG];
break;
case START:
[_controllerSupport setButtonFlag:_controller flags:PLAY_FLAG];
break;
case UP:
[_controllerSupport setButtonFlag:_controller flags:UP_FLAG];
break;
case RIGHT:
[_controllerSupport setButtonFlag:_controller flags:RIGHT_FLAG];
break;
case DOWN:
[_controllerSupport setButtonFlag:_controller flags:DOWN_FLAG];
break;
case LEFT:
[_controllerSupport setButtonFlag:_controller flags:LEFT_FLAG];
break;
case LB:
[_controllerSupport setButtonFlag:_controller flags:LB_FLAG];
break;
case RB:
[_controllerSupport setButtonFlag:_controller flags:RB_FLAG];
break;
case Y:
[_controllerSupport setButtonFlag:_controller flags:Y_FLAG];
break;
case B:
[_controllerSupport setButtonFlag:_controller flags:B_FLAG];
break;
case A:
[_controllerSupport setButtonFlag:_controller flags:A_FLAG];
break;
case X:
[_controllerSupport setButtonFlag:_controller flags:X_FLAG];
break;
default:
break;
}
[_controllerSupport updateFinished:_controller];
}
void onButtonUp(struct Gamepad_device * device, unsigned int buttonID, double timestamp, void * context) {
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
switch (buttonID) {
case SELECT:
[_controllerSupport clearButtonFlag:_controller flags:BACK_FLAG];
break;
case L3:
[_controllerSupport clearButtonFlag:_controller flags:LS_CLK_FLAG];
break;
case R3:
[_controllerSupport clearButtonFlag:_controller flags:RS_CLK_FLAG];
break;
case START:
[_controllerSupport clearButtonFlag:_controller flags:PLAY_FLAG];
break;
case UP:
[_controllerSupport clearButtonFlag:_controller flags:UP_FLAG];
break;
case RIGHT:
[_controllerSupport clearButtonFlag:_controller flags:RIGHT_FLAG];
break;
case DOWN:
[_controllerSupport clearButtonFlag:_controller flags:DOWN_FLAG];
break;
case LEFT:
[_controllerSupport clearButtonFlag:_controller flags:LEFT_FLAG];
break;
case LB:
[_controllerSupport clearButtonFlag:_controller flags:LB_FLAG];
break;
case RB:
[_controllerSupport clearButtonFlag:_controller flags:RB_FLAG];
break;
case Y:
[_controllerSupport clearButtonFlag:_controller flags:Y_FLAG];
break;
case B:
[_controllerSupport clearButtonFlag:_controller flags:B_FLAG];
break;
case A:
[_controllerSupport clearButtonFlag:_controller flags:A_FLAG];
break;
case X:
[_controllerSupport clearButtonFlag:_controller flags:X_FLAG];
break;
default:
break;
}
[_controllerSupport updateFinished:_controller];
}
void onAxisMoved(struct Gamepad_device * device, unsigned int axisID, float value, float lastValue, double timestamp, void * context) {
if (fabsf(lastValue - value) > 0.01) {
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
// The dualshock controller has much more than these axis because of the motion axis, so it
// is better to call the updateFinished in the cases, because otherwise all of these
// motion axis will also trigger an updateFinished event.
switch (axisID) {
case LEFT_X:
_controller.lastLeftStickX = value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case LEFT_Y:
_controller.lastLeftStickY = -value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case RIGHT_X:
_controller.lastRightStickX = value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case RIGHT_Y:
_controller.lastRightStickY = -value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case LT:
_controller.lastLeftTrigger = value * 0xFF;
[_controllerSupport updateFinished:_controller];
break;
case RT:
_controller.lastRightTrigger = value * 0xFF;
[_controllerSupport updateFinished:_controller];
break;
default:
break;
}
}
}
void onDeviceAttached(struct Gamepad_device * device, void * context) {
[_controllerSupport assignGamepad:device];
_controllers = [_controllerSupport getControllers];
}
void onDeviceRemoved(struct Gamepad_device * device, void * context) {
[_controllerSupport removeGamepad:device];
_controllers = [_controllerSupport getControllers];
}
void initGamepad(ControllerSupport* controllerSupport) {
_controllerSupport = controllerSupport;
Gamepad_deviceAttachFunc(onDeviceAttached, NULL);
Gamepad_deviceRemoveFunc(onDeviceRemoved, NULL);
Gamepad_buttonDownFunc(onButtonDown, NULL);
Gamepad_buttonUpFunc(onButtonUp, NULL);
Gamepad_axisMoveFunc(onAxisMoved, NULL);
Gamepad_init();
_controller = [[Controller alloc] init];
}

View File

@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View File

@@ -0,0 +1,12 @@
//
// StreamView.h
// Moonlight macOS
//
// Created by Felix Kratz on 10.03.18.
// Copyright (c) 2018 Felix Kratz. All rights reserved.
//
@interface StreamView : NSView
@end

View File

@@ -0,0 +1,114 @@
//
// StreamView.m
// Moonlight macOS
//
// Created by Felix Kratz on 10.3.18.
// Copyright (c) 2018 Felix Kratz. All rights reserved.
//
#import "StreamView.h"
#include <Limelight.h>
#import "DataManager.h"
#include <ApplicationServices/ApplicationServices.h>
#include "keyboardTranslation.h"
@implementation StreamView {
BOOL isDragging;
NSTrackingArea *trackingArea;
}
- (void) updateTrackingAreas {
// This will be the area used to track the mouse movement
if (trackingArea != nil) {
[self removeTrackingArea:trackingArea];
}
NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect |
NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
options:options
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
}
-(void)mouseDragged:(NSEvent *)event {
if (isDragging) {
[self mouseMoved:event];
}
else {
[self mouseDown:event];
isDragging = true;
}
}
-(void)rightMouseDragged:(NSEvent *)event {
if (isDragging) {
[self mouseMoved:event];
}
else {
[self rightMouseDown:event];
isDragging = true;
}
}
-(void)scrollWheel:(NSEvent *)event {
LiSendScrollEvent(event.scrollingDeltaY);
}
- (void)mouseDown:(NSEvent *)mouseEvent {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_LEFT);
[self setNeedsDisplay:YES];
}
- (void)mouseUp:(NSEvent *)mouseEvent {
isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_LEFT);
[self setNeedsDisplay:YES];
}
- (void)rightMouseUp:(NSEvent *)mouseEvent {
isDragging = false;
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, BUTTON_RIGHT);
[self setNeedsDisplay:YES];
}
- (void)rightMouseDown:(NSEvent *)mouseEvent {
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, BUTTON_RIGHT);
[self setNeedsDisplay:YES];
}
- (void)mouseMoved:(NSEvent *)mouseEvent {
LiSendMouseMoveEvent(mouseEvent.deltaX, mouseEvent.deltaY);
}
-(void)keyDown:(NSEvent *)event {
unsigned char keyChar = keyCharFromKeyCode(event.keyCode);
printf("DOWN: KeyCode: %hu, keyChar: %d, keyModifier: %lu \n", event.keyCode, keyChar, event.modifierFlags);
LiSendKeyboardEvent(keyChar, KEY_ACTION_DOWN, modifierFlagForKeyModifier(event.modifierFlags));
}
-(void)keyUp:(NSEvent *)event {
unsigned char keyChar = keyCharFromKeyCode(event.keyCode);
printf("UP: KeyChar: %d \n", keyChar);
LiSendKeyboardEvent(keyChar, KEY_ACTION_UP, modifierFlagForKeyModifier(event.modifierFlags));
}
- (void)flagsChanged:(NSEvent *)event
{
unsigned char keyChar = keyCodeFromModifierKey(event.modifierFlags);
if(keyChar) {
printf("DOWN: FlagChanged: %hhu \n", keyChar);
LiSendKeyboardEvent(keyChar, KEY_ACTION_DOWN, 0x00);
}
else {
LiSendKeyboardEvent(58, KEY_ACTION_UP, 0x00);
}
}
- (BOOL)acceptsFirstResponder {
return YES;
}
@end

View File

@@ -0,0 +1,18 @@
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.device.bluetooth</key>
<true/>
<key>com.apple.security.device.usb</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,20 @@
//
// keepAlive.h
// LittleHelper
//
// Created by Felix Kratz on 31.10.17.
// Copyright © 2017 Felix Kratz. All rights reserved.
//
#import <Foundation/Foundation.h>
#ifndef keepAlive_h
#define keepAlive_h
@interface keepAlive : NSObject
+(void) keepSystemAlive;
+(void) allowSleep;
@end
#endif /* keepAlive_h */

View File

@@ -0,0 +1,30 @@
//
// keepAlive.m
// LittleHelper
//
// Created by Felix Kratz on 31.10.17.
// Copyright © 2017 Felix Kratz. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <IOKit/pwr_mgt/IOPMLib.h>
#import "keepAlive.h"
@implementation keepAlive
CFStringRef reasonForActivity= CFSTR("Moonlight keeps the system awake");
IOPMAssertionID assertionID;
+(void) keepSystemAlive
{
IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep,
kIOPMAssertionLevelOn, reasonForActivity, &assertionID);
}
+(void) allowSleep
{
IOPMAssertionRelease(assertionID);
}
@end

View File

@@ -0,0 +1,18 @@
//
// keyboardTranslation.h
// Moonlight macOS
//
// Created by Felix Kratz on 10.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#include <CoreFoundation/CoreFoundation.h>
#include <Carbon/Carbon.h>
#ifndef keyboardTranslation_h
#define keyboardTranslation_h
CGKeyCode keyCharFromKeyCode(CGKeyCode keyCode);
CGKeyCode keyCodeFromModifierKey(NSEventModifierFlags keyModifier);
char modifierFlagForKeyModifier(NSEventModifierFlags keyModifier);
#endif /* keyboardTranslation_h */

View File

@@ -0,0 +1,143 @@
//
// keyboardTranslation.m
// Moonlight macOS
//
// Created by Felix Kratz on 10.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "keyboardTranslation.h"
#import <Limelight.h>
typedef enum {
NOKEY,
VK_BACKSPACE = 0x08,
VK_TAB = 0x09,
VK_ENTER = 0x0D,
VK_SPACE = 0x20,
VK_LEFT = 0x25,
VK_UP,
VK_RIGHT,
VK_DOWN,
VK_SHIFT = 0xA0,
VK_CTRL = 0xA2,
VK_ALT = 0xA4,
VK_COMMA = 0xBC,
VK_MINUS = 0xBD,
VK_PERIOD = 0xBE,
VK_ESC = 0x1B,
VK_F1 = 0x70,
VK_F2,
VK_F3,
VK_F4,
VK_F5,
VK_F6,
VK_F7,
VK_F8,
VK_F9,
VK_F10,
VK_F11,
VK_F12,
VK_F13,
VK_F14,
VK_F15,
VK_DEL = 0x7F,
} SpecialKeyCodes;
CGKeyCode keyCodeFromModifierKey(NSEventModifierFlags keyModifier) {
if (keyModifier & kCGEventFlagMaskShift) {
return VK_SHIFT;
}
if (keyModifier & kCGEventFlagMaskAlternate) {
return VK_ALT;
}
if (keyModifier & kCGEventFlagMaskControl) {
return VK_CTRL;
}
return NOKEY;
}
char modifierFlagForKeyModifier(NSEventModifierFlags keyModifier) {
if (keyModifier & kCGEventFlagMaskShift) {
return MODIFIER_SHIFT;
}
if (keyModifier & kCGEventFlagMaskAlternate) {
return MODIFIER_ALT;
}
if (keyModifier & kCGEventFlagMaskControl) {
return MODIFIER_CTRL;
}
return NOKEY;
}
CGKeyCode keyCharFromKeyCode(CGKeyCode keyCode) {
switch (keyCode) {
case kVK_ANSI_A: return 'A';
case kVK_ANSI_S: return 'S';
case kVK_ANSI_D: return 'D';
case kVK_ANSI_F: return 'F';
case kVK_ANSI_H: return 'H';
case kVK_ANSI_G: return 'G';
case kVK_ANSI_Y: return 'Y';
case kVK_ANSI_X: return 'X';
case kVK_ANSI_C: return 'C';
case kVK_ANSI_V: return 'V';
case kVK_ANSI_B: return 'B';
case kVK_ANSI_Q: return 'Q';
case kVK_ANSI_W: return 'W';
case kVK_ANSI_E: return 'E';
case kVK_ANSI_R: return 'R';
case kVK_ANSI_Z: return 'Z';
case kVK_ANSI_T: return 'T';
case kVK_ANSI_1: return '1';
case kVK_ANSI_2: return '2';
case kVK_ANSI_3: return '3';
case kVK_ANSI_4: return '4';
case kVK_ANSI_6: return '6';
case kVK_ANSI_5: return '5';
case kVK_ANSI_9: return '9';
case kVK_ANSI_7: return '7';
case kVK_ANSI_8: return '8';
case kVK_ANSI_0: return '0';
case kVK_ANSI_O: return 'O';
case kVK_ANSI_U: return 'U';
case kVK_ANSI_I: return 'I';
case kVK_ANSI_P: return 'P';
case kVK_ANSI_L: return 'L';
case kVK_ANSI_J: return 'J';
case kVK_ANSI_K: return 'K';
case kVK_ANSI_N: return 'N';
case kVK_ANSI_M: return 'M';
case kVK_Return: return VK_ENTER;
case kVK_ANSI_Period: return VK_PERIOD;
case kVK_ANSI_Comma: return VK_COMMA;
case kVK_ANSI_Minus: return VK_MINUS;
case kVK_Tab: return VK_TAB;
case kVK_Space: return VK_SPACE;
case kVK_Delete: return VK_BACKSPACE;
case kVK_Escape: return VK_ESC;
case kVK_F1: return VK_F1;
case kVK_F2: return VK_F2;
case kVK_F3: return VK_F3;
case kVK_F4: return VK_F4;
case kVK_F5: return VK_F5;
case kVK_F6: return VK_F6;
case kVK_F7: return VK_F7;
case kVK_F8: return VK_F8;
case kVK_F9: return VK_F9;
case kVK_F10: return VK_F10;
case kVK_F11: return VK_F11;
case kVK_F12: return VK_F12;
case kVK_F13: return VK_F13;
case kVK_F14: return VK_F14;
case kVK_F15: return VK_F15;
case kVK_LeftArrow: return VK_LEFT;
case kVK_RightArrow: return VK_RIGHT;
case kVK_DownArrow: return VK_DOWN;
case kVK_UpArrow: return VK_UP;
default:
return NOKEY;
}
}

View File

@@ -0,0 +1,23 @@
//
// SettingsViewController.h
// Moonlight macOS
//
// Created by Felix Kratz on 11.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface SettingsViewController : NSViewController
@property (weak) IBOutlet NSTextField *textFieldResolutionHeight;
@property (weak) IBOutlet NSTextField *textFieldResolutionWidth;
@property (weak) IBOutlet NSTextField *textFieldBitrate;
@property (weak) IBOutlet NSTextField *textFieldFPS;
@property (weak) IBOutlet NSButton *buttonStreamingRemotelyToggle;
- (NSString*) getCurrentHost;
- (NSInteger) getChosenBitrate;
- (NSInteger) getChosenStreamWidth;
- (NSInteger) getChosenStreamHeight;
- (NSInteger) getChosenFrameRate;
- (NSInteger) getRemoteOptions;
@end

View File

@@ -0,0 +1,71 @@
//
// SettingsViewController.m
// Moonlight macOS
//
// Created by Felix Kratz on 11.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "SettingsViewController.h"
#import "DataManager.h"
@interface SettingsViewController ()
@end
@implementation SettingsViewController
NSString* host;
- (void)viewDidLoad {
[super viewDidLoad];
[self loadSettings];
// Do view setup here.
}
- (void) controlTextDidChange:(NSNotification *)obj {
//[self saveSettings];
}
- (NSInteger) getRemoteOptions {
return _buttonStreamingRemotelyToggle.state == NSOnState ? 1 : 0;
}
- (NSInteger) getChosenFrameRate {
return _textFieldFPS.integerValue;
}
- (NSInteger) getChosenStreamHeight {
return _textFieldResolutionHeight.integerValue;
}
- (NSInteger) getChosenStreamWidth {
return _textFieldResolutionWidth.integerValue;
}
- (NSInteger) getChosenBitrate {
return _textFieldBitrate.integerValue;
}
- (void) loadSettings {
DataManager* dataMan = [[DataManager alloc] init];
TemporarySettings* currentSettings = [dataMan getSettings];
// Bitrate is persisted in kbps
_textFieldBitrate.integerValue = [currentSettings.bitrate integerValue];
_textFieldFPS.integerValue = [currentSettings.framerate integerValue];
_textFieldResolutionHeight.integerValue = [currentSettings.height integerValue];
_textFieldResolutionWidth.integerValue = [currentSettings.width integerValue];
_buttonStreamingRemotelyToggle.state = [currentSettings.streamingRemotely integerValue] == 0 ? NSOffState: NSOnState;
}
- (void)didReceiveMemoryWarning {
// Dispose of any resources that can be recreated.
}
-(NSString*) getCurrentHost {
return host;
}
@end

View File

@@ -0,0 +1,26 @@
//
// StreamFrameViewController.h
// Moonlight macOS
//
// Created by Felix Kratz on 09.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import "Connection.h"
#import "StreamConfiguration.h"
#import "StreamView.h"
#import "ViewController.h"
@interface StreamFrameViewController : NSViewController <ConnectionCallbacks>
- (ViewController*) _origin;
- (void)setOrigin: (ViewController*) viewController;
@property (nonatomic) StreamConfiguration* streamConfig;
@property (strong) IBOutlet StreamView *streamView;
@property (weak) IBOutlet NSProgressIndicator *progressIndicator;
@property (weak) IBOutlet NSTextField *stageLabel;
@end

View File

@@ -0,0 +1,140 @@
//
// StreamFrameViewController.m
// Moonlight macOS
//
// Created by Felix Kratz on 09.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "StreamFrameViewController.h"
#import "VideoDecoderRenderer.h"
#import "StreamManager.h"
#import "Gamepad.h"
#import "keepAlive.h"
#import "ControllerSupport.h"
@interface StreamFrameViewController ()
@end
@implementation StreamFrameViewController {
StreamManager *_streamMan;
StreamConfiguration *_streamConfig;
NSTimer* _eventTimer;
NSTimer* _searchTimer;
ViewController* _origin;
ControllerSupport* _controllerSupport;
}
-(ViewController*) _origin {
return _origin;
}
- (void)viewDidLoad {
[super viewDidLoad];
[keepAlive keepSystemAlive];
self.streamConfig = _streamConfig;
_streamMan = [[StreamManager alloc] initWithConfig:self.streamConfig
renderView:self.view
connectionCallbacks:self];
NSOperationQueue* opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperation:_streamMan];
// Initialize the controllers (GC and IOHID)
// I have not tested it, but i think there will be a bug, when mixing GC and IOHID Controllers, because all GCControllers also register as IOHID Controllers.
// To fix this, we need to get the IOHIDDeviceRef for the GCController and compare it with every IOHID Controller.
// This shouldn't be a problem as long as this will not be put on the mac app store, as the IOHIDDeviceRef is a private field.
// The other "fix" would be to disable explicit GC support for the mac version for now.
// Can someone test this?
_controllerSupport = [[ControllerSupport alloc] init];
// The gamepad currently gets polled at 60Hz, this could very well be set as 1/Framerate in the future.
_eventTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/60.0 target:self selector:@selector(eventTimerTick) userInfo:nil repeats:true];
// We search for new devices every second.
_searchTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(searchTimerTick) userInfo:nil repeats:true];
}
- (void)eventTimerTick {
Gamepad_processEvents();
}
- (void)searchTimerTick {
Gamepad_detectDevices();
}
- (void) viewDidAppear {
[super viewDidAppear];
// Hide the cursor and disconnect it from the mouse movement
[NSCursor hide];
CGAssociateMouseAndMouseCursorPosition(false);
//During the setup the window should not be resizable, but to use the fullscreen feature of macOS it has to be resizable.
[self.view.window setStyleMask:[self.view.window styleMask] | NSWindowStyleMaskResizable];
if (self.view.bounds.size.height != NSScreen.mainScreen.frame.size.height || self.view.bounds.size.width != NSScreen.mainScreen.frame.size.width) {
[self.view.window toggleFullScreen:self];
}
[_progressIndicator startAnimation:nil];
[_origin dismissController:nil];
_origin = nil;
}
-(void)viewWillDisappear {
[NSCursor unhide];
[keepAlive allowSleep];
[_streamMan stopStream];
CGAssociateMouseAndMouseCursorPosition(true);
if (self.view.bounds.size.height == NSScreen.mainScreen.frame.size.height && self.view.bounds.size.width == NSScreen.mainScreen.frame.size.width) {
[self.view.window toggleFullScreen:self];
[self.view.window setStyleMask:[self.view.window styleMask] & ~NSWindowStyleMaskResizable];
}
}
- (void)connectionStarted {
dispatch_async(dispatch_get_main_queue(), ^{
[_progressIndicator stopAnimation:nil];
_progressIndicator.hidden = true;
_stageLabel.stringValue = @"Waiting for the first frame";
});
}
- (void)connectionTerminated:(long)errorCode {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"error has occured: %ld", errorCode);
NSStoryboard *storyBoard = [NSStoryboard storyboardWithName:@"Mac" bundle:nil];
ViewController* view = (ViewController*)[storyBoard instantiateControllerWithIdentifier :@"setupFrameVC"];
[view setError:1];
self.view.window.contentViewController = view;
});
}
- (void)setOrigin: (ViewController*) viewController
{
_origin = viewController;
}
- (void)displayMessage:(const char *)message {
}
- (void)displayTransientMessage:(const char *)message {
}
- (void)launchFailed:(NSString *)message {
}
- (void)stageComplete:(const char *)stageName {
}
- (void)stageFailed:(const char *)stageName withError:(long)errorCode {
}
- (void)stageStarting:(const char *)stageName {
}
@end

View File

@@ -0,0 +1,35 @@
//
// ViewController.h
// Moonlight macOS
//
// Created by Felix Kratz on 09.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#import "PairManager.h"
#import "StreamConfiguration.h"
@interface ViewController : NSViewController <PairCallback, NSURLConnectionDelegate>
- (IBAction)buttonLaunchPressed:(id)sender;
- (IBAction)textFieldAction:(id)sender;
- (IBAction)buttonConnectPressed:(id)sender;
- (IBAction)buttonSettingsPressed:(id)sender;
- (IBAction)popupButtonSelectionPressed:(id)sender;
- (void)setError:(long)errorCode;
- (long) error;
@property (weak) IBOutlet NSLayoutConstraint *layoutConstraintSetupFrame;
@property (weak) IBOutlet NSButton *buttonConnect;
@property (weak) IBOutlet NSTextField *textFieldHost;
@property (weak) IBOutlet NSButton *buttonSettings;
@property (weak) IBOutlet NSButton *buttonLaunch;
@property (weak) IBOutlet NSPopUpButton *popupButtonSelection;
@property (weak) IBOutlet NSView *containerViewController;
@end

View File

@@ -0,0 +1,228 @@
//
// ViewController.m
// Moonlight macOS
//
// Created by Felix Kratz on 09.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "ViewController.h"
#import "CryptoManager.h"
#import "HttpManager.h"
#import "Connection.h"
#import "StreamManager.h"
#import "Utils.h"
#import "DataManager.h"
#import "TemporarySettings.h"
#import "WakeOnLanManager.h"
#import "AppListResponse.h"
#import "ServerInfoResponse.h"
#import "StreamFrameViewController.h"
#import "TemporaryApp.h"
#import "IdManager.h"
#import "SettingsViewController.h"
#import "ConnectionHelper.h"
@implementation ViewController{
NSOperationQueue* _opQueue;
TemporaryHost* _selectedHost;
NSString* _uniqueId;
NSData* _cert;
StreamConfiguration* _streamConfig;
NSArray* _sortedAppList;
NSSet* _appList;
NSString* _host;
SettingsViewController *_settingsView;
CGFloat settingsFrameHeight;
bool showSettings;
NSAlert* _alert;
long error;
}
- (long)error {
return error;
}
- (void)setError:(long)errorCode {
error = errorCode;
}
- (void)viewDidLoad {
[super viewDidLoad];
[CryptoManager generateKeyPairUsingSSl];
_uniqueId = [IdManager getUniqueId];
_cert = [CryptoManager readCertFromFile];
_opQueue = [[NSOperationQueue alloc] init];
// Do any additional setup after loading the view.
}
- (void)viewDidAppear {
[super viewDidAppear];
if (self.view.bounds.size.height == NSScreen.mainScreen.frame.size.height && self.view.bounds.size.width == NSScreen.mainScreen.frame.size.width)
{
[self.view.window toggleFullScreen:self];
[self.view.window setStyleMask:[self.view.window styleMask] & ~NSWindowStyleMaskResizable];
return;
}
[_buttonLaunch setEnabled:false];
[_popupButtonSelection removeAllItems];
_settingsView = [self.childViewControllers lastObject];
if (_settingsView.getCurrentHost != nil)
_textFieldHost.stringValue = _settingsView.getCurrentHost;
settingsFrameHeight = _layoutConstraintSetupFrame.constant;
_layoutConstraintSetupFrame.constant = 0;
showSettings = false;
if (error != 0) {
[self showAlert:[NSString stringWithFormat: @"The connection terminated."]];
}
}
-(void) showAlert:(NSString*) message {
dispatch_async(dispatch_get_main_queue(), ^{
_alert = [NSAlert new];
_alert.messageText = message;
[_alert beginSheetModalForWindow:[self.view window] completionHandler:^(NSInteger result) {
NSLog(@"Success");
}];
});
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
- (void) saveSettings {
DataManager* dataMan = [[DataManager alloc] init];
NSInteger framerate = [_settingsView getChosenFrameRate];
NSInteger height = [_settingsView getChosenStreamHeight];
NSInteger width = [_settingsView getChosenStreamWidth];
NSInteger streamingRemotely = [_settingsView getRemoteOptions];
NSInteger bitrate = [_settingsView getChosenBitrate];
[dataMan saveSettingsWithBitrate:bitrate framerate:framerate height:height width:width
onscreenControls:0 remote:streamingRemotely];
}
- (void)controlTextDidChange:(NSNotification *)obj {
}
- (IBAction)buttonLaunchPressed:(id)sender {
[self saveSettings];
DataManager* dataMan = [[DataManager alloc] init];
TemporarySettings* streamSettings = [dataMan getSettings];
_streamConfig = [[StreamConfiguration alloc] init];
_streamConfig.frameRate = [streamSettings.framerate intValue];
_streamConfig.bitRate = [streamSettings.bitrate intValue];
_streamConfig.height = [streamSettings.height intValue];
_streamConfig.width = [streamSettings.width intValue];
_streamConfig.streamingRemotely = [streamSettings.streamingRemotely intValue];
_streamConfig.host = _textFieldHost.stringValue;
_streamConfig.appID = [_sortedAppList[_popupButtonSelection.indexOfSelectedItem] id];
[self transitionToStreamView];
}
- (IBAction)textFieldAction:(id)sender {
[self buttonConnectPressed:self];
}
- (IBAction)buttonConnectPressed:(id)sender {
_host = _textFieldHost.stringValue;
HttpManager* hMan = [[HttpManager alloc] initWithHost:_textFieldHost.stringValue
uniqueId:_uniqueId
deviceName:@"roth"
cert:_cert];
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
[hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResp withUrlRequest:[hMan newServerInfoRequest]
fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]];
if ([[serverInfoResp getStringTag:@"PairStatus"] isEqualToString:@"1"]) {
NSLog(@"alreadyPaired");
[self alreadyPaired];
} else {
// Polling the server while pairing causes the server to screw up
NSLog(@"Pairing");
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
HttpManager* hMan = [[HttpManager alloc] initWithHost:_host uniqueId:_uniqueId deviceName:deviceName cert:_cert];
PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:_cert callback:self];
[_opQueue addOperation:pMan];
});
}
}
- (IBAction)buttonSettingsPressed:(id)sender {
showSettings = !showSettings;
if(showSettings) {
_layoutConstraintSetupFrame.constant = settingsFrameHeight;
}
else {
_layoutConstraintSetupFrame.constant = 0;
}
}
- (IBAction)popupButtonSelectionPressed:(id)sender {
}
- (void)alreadyPaired {
dispatch_async(dispatch_get_main_queue(), ^{
[_popupButtonSelection setEnabled:true];
[_popupButtonSelection setHidden:false];
[_buttonConnect setEnabled:false];
[_buttonConnect setHidden:true];
[_buttonLaunch setEnabled:true];
[_textFieldHost setEnabled:false];
});
[self searchForHost:_host];
[self updateAppsForHost];
[self populatePopupButton];
}
- (void)searchForHost:(NSString*) hostAddress {
_appList = [ConnectionHelper getAppListForHostWithHostIP:_textFieldHost.stringValue deviceName:deviceName cert:_cert uniqueID:_uniqueId].getAppList;
}
- (void)populatePopupButton {
for (int i = 0; i < _appList.count; i++) {
[_popupButtonSelection addItemWithTitle:[_sortedAppList[i] name]];
}
}
- (void)pairFailed:(NSString *)message {
[self showAlert:[NSString stringWithFormat: @"%@", message]];
}
- (void)pairSuccessful {
dispatch_async(dispatch_get_main_queue(), ^{
[self.view.window endSheet:_alert.window];
_alert = nil;
[self alreadyPaired];
});
}
- (void)showPIN:(NSString *)PIN {
[self showAlert:[NSString stringWithFormat: @"PIN: %@", PIN]];
}
- (void) updateAppsForHost {
_sortedAppList = [_appList allObjects];
_sortedAppList = [_sortedAppList sortedArrayUsingSelector:@selector(compareName:)];
}
- (void)transitionToStreamView {
NSStoryboard *storyBoard = [NSStoryboard storyboardWithName:@"Mac" bundle:nil];
StreamFrameViewController* streamFrame = (StreamFrameViewController*)[storyBoard instantiateControllerWithIdentifier :@"streamFrameVC"];
streamFrame.streamConfig = _streamConfig;
[streamFrame setOrigin:self];
self.view.window.contentViewController = streamFrame;
}
@end

13
Moonlight macOS/main.m Normal file
View File

@@ -0,0 +1,13 @@
//
// main.m
// Moonlight macOS
//
// Created by Felix on 09.03.18.
// Copyright © 2018 Felix. All rights reserved.
//
#import <Cocoa/Cocoa.h>
int main(int argc, const char * argv[]) {
return NSApplicationMain(argc, argv);
}