Dark Mode & Stream Overlay [macOS] (#315)

* dark mode & stream overlay

* removed all redundant imports

* update for the new xcode version with fixes for the new 'implicitly retains self warning'

* reworked the overlay view

* cleaning up unused variables

* small corrections
This commit is contained in:
Felix Kratz
2018-04-22 06:44:22 +02:00
committed by Cameron Gutman
parent 74283a6763
commit f759f719e6
50 changed files with 358 additions and 146 deletions
+7 -2
View File
@@ -160,32 +160,37 @@ void onButtonUp(struct Gamepad_device * device, unsigned int buttonID, double ti
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 = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
_controller.lastLeftStickX = value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case LEFT_Y:
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
_controller.lastLeftStickY = -value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case RIGHT_X:
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
_controller.lastRightStickX = value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case RIGHT_Y:
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
_controller.lastRightStickY = -value * 0X7FFE;
[_controllerSupport updateFinished:_controller];
break;
case LT:
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
_controller.lastLeftTrigger = value * 0xFF;
[_controllerSupport updateFinished:_controller];
break;
case RT:
_controller = [_controllers objectForKey:[NSNumber numberWithInteger:device->deviceID]];
_controller.lastRightTrigger = value * 0xFF;
[_controllerSupport updateFinished:_controller];
break;
@@ -208,11 +213,11 @@ void onDeviceRemoved(struct Gamepad_device * device, void * context) {
void initGamepad(ControllerSupport* controllerSupport) {
_controllerSupport = controllerSupport;
_controller = [[Controller alloc] init];
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];
}
+16
View File
@@ -0,0 +1,16 @@
//
// OverlayView.h
// Moonlight macOS
//
// Created by Felix Kratz on 01.04.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface OverlayView : NSView
- (id)initWithFrame:(NSRect)frame sender:(StreamView*)sender;
- (void)toggleOverlay:(int)codec;
@end
+98
View File
@@ -0,0 +1,98 @@
//
// OverlayView.m
// Moonlight macOS
//
// Created by Felix Kratz on 01.04.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "StreamView.h"
#import "OverlayView.h"
#import "NetworkTraffic.h"
@implementation OverlayView {
StreamView* _streamView;
bool statsDisplayed;
unsigned long lastNetworkDown;
unsigned long lastNetworkUp;
NSTextField* _textFieldIncomingBitrate;
NSTextField* _textFieldOutgoingBitrate;
NSTextField* _textFieldCodec;
NSTextField* _textFieldFramerate;
NSTextField* _stageLabel;
NSTimer* _statTimer;
}
- (id)initWithFrame:(NSRect)frame sender:(StreamView*)sender
{
self = [super initWithFrame:frame];
if (self) {
_streamView = sender;
}
return self;
}
- (void)initStats {
_textFieldCodec = [[NSTextField alloc] initWithFrame:NSMakeRect(5, NSScreen.mainScreen.frame.size.height - 22, 200, 17)];
_textFieldIncomingBitrate = [[NSTextField alloc] initWithFrame:NSMakeRect(5, 5, 250, 17)];
_textFieldOutgoingBitrate = [[NSTextField alloc] initWithFrame:NSMakeRect(5, 5 + 20, 250, 17)];
_textFieldFramerate = [[NSTextField alloc] initWithFrame:NSMakeRect(NSScreen.mainScreen.frame.size.width - 50, NSScreen.mainScreen.frame.size.height - 22, 50, 17)];
[self setupTextField:_textFieldOutgoingBitrate];
[self setupTextField:_textFieldIncomingBitrate];
[self setupTextField:_textFieldCodec];
[self setupTextField:_textFieldFramerate];
}
- (void)setupTextField:(NSTextField*)textField {
textField.drawsBackground = false;
textField.bordered = false;
textField.editable = false;
textField.alignment = NSTextAlignmentLeft;
textField.textColor = [NSColor whiteColor];
[self addSubview:textField];
}
- (void)toggleOverlay:(int)codec {
statsDisplayed = !statsDisplayed;
if (statsDisplayed) {
_streamView.frameCount = 0;
if (_textFieldIncomingBitrate == nil || _textFieldCodec == nil || _textFieldOutgoingBitrate == nil || _textFieldFramerate == nil) {
[self initStats];
}
_statTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(statTimerTick) userInfo:nil repeats:true];
NSLog(@"display stats");
if (codec == 1) {
_textFieldCodec.stringValue = @"Codec: H.264";
}
else if (codec == 256) {
_textFieldCodec.stringValue = @"Codec: HEVC/H.265";
}
else {
_textFieldCodec.stringValue = @"Codec: Unknown";
}
[self statTimerTick];
}
else {
[_statTimer invalidate];
_textFieldCodec.stringValue = @"";
_textFieldIncomingBitrate.stringValue = @"";
_textFieldOutgoingBitrate.stringValue = @"";
_textFieldFramerate.stringValue = @"";
}
}
- (void)statTimerTick {
_textFieldFramerate.stringValue = [NSString stringWithFormat:@"%i fps", _streamView.frameCount];
_streamView.frameCount = 0;
unsigned long currentNetworkDown = getBytesDown();
_textFieldIncomingBitrate.stringValue = [NSString stringWithFormat:@"Incoming Bitrate (System): %lu kbps", (currentNetworkDown - lastNetworkDown)*8 / 1000];
lastNetworkDown = currentNetworkDown;
unsigned long currentNetworkUp = getBytesUp();
_textFieldOutgoingBitrate.stringValue = [NSString stringWithFormat:@"Outgoing Bitrate (System): %lu kbps", (currentNetworkUp - lastNetworkUp)*8 / 1000];
lastNetworkUp = currentNetworkUp;
}
@end
+5
View File
@@ -8,5 +8,10 @@
@interface StreamView : NSView
- (void)drawMessage:(NSString*)message;
@property int codec;
@property unsigned short frameCount;
@end
+43 -17
View File
@@ -11,26 +11,27 @@
#import "DataManager.h"
#include <ApplicationServices/ApplicationServices.h>
#include "keyboardTranslation.h"
#import "OverlayView.h"
@implementation StreamView {
BOOL isDragging;
NSTrackingArea *trackingArea;
bool isDragging;
NSTrackingArea* _trackingArea;
OverlayView* _overlay;
NSTextField* _stageLabel;
}
- (void) updateTrackingAreas {
// This will be the area used to track the mouse movement
if (trackingArea != nil) {
[self removeTrackingArea:trackingArea];
if (_trackingArea != nil) {
[self removeTrackingArea:_trackingArea];
}
NSTrackingAreaOptions options = (NSTrackingActiveAlways | NSTrackingInVisibleRect |
NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved);
trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
_trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds]
options:options
owner:self
userInfo:nil];
[self addTrackingArea:trackingArea];
[self addTrackingArea:_trackingArea];
}
-(void)mouseDragged:(NSEvent *)event {
@@ -59,24 +60,20 @@
- (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 {
@@ -85,22 +82,24 @@
-(void)keyDown:(NSEvent *)event {
unsigned char keyChar = keyCharFromKeyCode(event.keyCode);
printf("DOWN: KeyCode: %hu, keyChar: %d, keyModifier: %lu \n", event.keyCode, keyChar, event.modifierFlags);
NSLog(@"DOWN: KeyCode: %hu, keyChar: %d, keyModifier: %lu \n", event.keyCode, keyChar, event.modifierFlags);
LiSendKeyboardEvent(keyChar, KEY_ACTION_DOWN, modifierFlagForKeyModifier(event.modifierFlags));
if (event.modifierFlags & kCGEventFlagMaskCommand && event.keyCode == kVK_ANSI_I) {
[self toggleStats];
}
}
-(void)keyUp:(NSEvent *)event {
unsigned char keyChar = keyCharFromKeyCode(event.keyCode);
printf("UP: KeyChar: %d \n", keyChar);
NSLog(@"UP: KeyChar: %d \n", keyChar);
LiSendKeyboardEvent(keyChar, KEY_ACTION_UP, modifierFlagForKeyModifier(event.modifierFlags));
}
- (void)flagsChanged:(NSEvent *)event
{
- (void)flagsChanged:(NSEvent *)event {
unsigned char keyChar = keyCodeFromModifierKey(event.modifierFlags);
if(keyChar) {
printf("DOWN: FlagChanged: %hhu \n", keyChar);
NSLog(@"DOWN: FlagChanged: %hhu \n", keyChar);
LiSendKeyboardEvent(keyChar, KEY_ACTION_DOWN, 0x00);
}
else {
@@ -108,6 +107,33 @@
}
}
- (void)initStageLabel {
_stageLabel = [[NSTextField alloc] initWithFrame:NSMakeRect(NSScreen.mainScreen.frame.size.width/2 - 100, NSScreen.mainScreen.frame.size.height/2 - 8, 200, 17)];
_stageLabel.drawsBackground = false;
_stageLabel.bordered = false;
_stageLabel.alignment = NSTextAlignmentCenter;
_stageLabel.textColor = [NSColor blackColor];
[self addSubview:_stageLabel];
}
- (void)toggleStats {
if (_overlay == nil) {
_overlay = [[OverlayView alloc] initWithFrame:self.frame sender:self];
[self addSubview:_overlay];
}
[_overlay toggleOverlay:_codec];
}
- (void)drawMessage:(NSString*)message {
dispatch_async(dispatch_get_main_queue(), ^{
if (self->_stageLabel == nil) {
[self initStageLabel];
}
self->_stageLabel.stringValue = message;
});
}
- (BOOL)acceptsFirstResponder {
return YES;
}
+15
View File
@@ -0,0 +1,15 @@
//
// NetworkTraffic.h
// Moonlight macOS
//
// Created by Felix Kratz on 28.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#ifndef NetworkTraffic_h
#define NetworkTraffic_h
unsigned long getBytesDown(void);
unsigned long getBytesUp(void);
#endif /* NetworkTraffic_h */
+50
View File
@@ -0,0 +1,50 @@
//
// NetworkTraffic.m
// Moonlight macOS
//
// Created by Felix Kratz on 28.03.18.
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import "NetworkTraffic.h"
#include <ifaddrs.h>
#include <net/if.h>
struct ifaddrs *ifap, *ifa;
unsigned long da;
unsigned long getBytesDown() {
da = 0;
getifaddrs (&ifap);
ifa = ifap;
while (ifa != NULL) {
if (ifa->ifa_addr->sa_family == AF_LINK) {
const struct if_data *ifa_data = (struct if_data *)ifa->ifa_data;
if (ifa_data != NULL) {
da += ifa_data->ifi_ibytes;
}
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifap);
return da;
}
unsigned long getBytesUp() {
da = 0;
getifaddrs (&ifap);
ifa = ifap;
while (ifa != NULL) {
if (ifa->ifa_addr->sa_family == AF_LINK) {
const struct if_data *ifa_data = (struct if_data *)ifa->ifa_data;
if (ifa_data != NULL) {
da += ifa_data->ifi_obytes;
}
}
ifa = ifa->ifa_next;
}
freeifaddrs(ifap);
return da;
}
-1
View File
@@ -5,7 +5,6 @@
// 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
@@ -6,7 +6,6 @@
// Copyright © 2018 Felix Kratz. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "keyboardTranslation.h"
#import <Limelight.h>
@@ -21,6 +21,5 @@
@property (nonatomic) StreamConfiguration* streamConfig;
@property (strong) IBOutlet StreamView *streamView;
@property (weak) IBOutlet NSProgressIndicator *progressIndicator;
@property (weak) IBOutlet NSTextField *stageLabel;
@end
@@ -12,6 +12,7 @@
#import "Gamepad.h"
#import "keepAlive.h"
#import "ControllerSupport.h"
#import "StreamView.h"
@interface StreamFrameViewController ()
@end
@@ -48,11 +49,11 @@
// 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];
// The gamepad currently gets polled at 1/Framerate.
_eventTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/_streamConfig.frameRate 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];
// We search for new devices every 2 seconds.
_searchTimer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(searchTimerTick) userInfo:nil repeats:true];
}
- (void)eventTimerTick {
@@ -94,36 +95,38 @@
- (void)connectionStarted {
dispatch_async(dispatch_get_main_queue(), ^{
[_progressIndicator stopAnimation:nil];
_progressIndicator.hidden = true;
_stageLabel.stringValue = @"Waiting for the first frame";
[self->_progressIndicator stopAnimation:nil];
self->_progressIndicator.hidden = true;
});
}
- (void)connectionTerminated:(long)errorCode {
[_streamMan stopStream];
[self transitionToSetupView:1];
}
- (void)transitionToSetupView:(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];
[view setError:errorCode];
self.view.window.contentViewController = view;
});
}
- (void)setOrigin: (ViewController*) viewController
{
- (void)setOrigin: (ViewController*) viewController {
_origin = viewController;
}
- (void)displayMessage:(const char *)message {
//[_streamView drawMessage:[NSString stringWithFormat:@"%s", message]];
}
- (void)displayTransientMessage:(const char *)message {
}
- (void)launchFailed:(NSString *)message {
}
- (void)stageComplete:(const char *)stageName {
@@ -131,10 +134,11 @@
}
- (void)stageFailed:(const char *)stageName withError:(long)errorCode {
//[_streamView drawMessage:[NSString stringWithFormat:@"Stage: %s failed with code: %li", stageName, errorCode]];
}
- (void)stageStarting:(const char *)stageName {
//[_streamView drawMessage:[NSString stringWithFormat:@"%s", stageName]];
}
@end
@@ -7,7 +7,6 @@
//
#import <Cocoa/Cocoa.h>
#import <AppKit/AppKit.h>
#import "PairManager.h"
#import "StreamConfiguration.h"
+23 -15
View File
@@ -58,6 +58,14 @@
// Do any additional setup after loading the view.
}
- (void)viewWillAppear {
[super viewWillAppear];
if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"] isEqual: @"Dark"]) {
[self.view.window setAppearance:[NSAppearance appearanceNamed:NSAppearanceNameVibrantDark]];
}
}
- (void)viewDidAppear {
[super viewDidAppear];
@@ -84,9 +92,9 @@
-(void) showAlert:(NSString*) message {
dispatch_async(dispatch_get_main_queue(), ^{
_alert = [NSAlert new];
_alert.messageText = message;
[_alert beginSheetModalForWindow:[self.view window] completionHandler:^(NSInteger result) {
self->_alert = [NSAlert new];
self->_alert.messageText = message;
[self->_alert beginSheetModalForWindow:[self.view window] completionHandler:^(NSInteger result) {
NSLog(@"Success");
}];
});
@@ -138,7 +146,7 @@
_host = _textFieldHost.stringValue;
HttpManager* hMan = [[HttpManager alloc] initWithHost:_textFieldHost.stringValue
uniqueId:_uniqueId
deviceName:@"roth"
deviceName:deviceName
cert:_cert];
ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init];
@@ -152,9 +160,9 @@
// 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];
HttpManager* hMan = [[HttpManager alloc] initWithHost:self->_host uniqueId:self->_uniqueId deviceName:deviceName cert:self->_cert];
PairManager* pMan = [[PairManager alloc] initWithManager:hMan andCert:self->_cert callback:self];
[self->_opQueue addOperation:pMan];
});
}
}
@@ -174,12 +182,12 @@
- (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->_popupButtonSelection setEnabled:true];
[self->_popupButtonSelection setHidden:false];
[self->_buttonConnect setEnabled:false];
[self->_buttonConnect setHidden:true];
[self->_buttonLaunch setEnabled:true];
[self->_textFieldHost setEnabled:false];
});
[self searchForHost:_host];
[self updateAppsForHost];
@@ -202,8 +210,8 @@
- (void)pairSuccessful {
dispatch_async(dispatch_get_main_queue(), ^{
[self.view.window endSheet:_alert.window];
_alert = nil;
[self.view.window endSheet:self->_alert.window];
self->_alert = nil;
[self alreadyPaired];
});
}