From d69688678d36f2a0a62cdfb908d036d15930eb67 Mon Sep 17 00:00:00 2001 From: Acaki Date: Sat, 12 Jul 2025 02:00:33 +0800 Subject: [PATCH] fix: build --- .gitmodules | 2 +- ImGui/imgui_impl_metal.h | 78 ++ Limelight/Database/DataManager.h | 0 Limelight/Database/TemporarySettings.h | 40 - Limelight/Input/RelativeTouchHandler.h | 0 Limelight/Input/RelativeTouchHandler.m | 0 Limelight/Input/StreamView.h | 37 - .../ViewControllers/SettingsViewController.m | 2 - .../StreamFrameViewController.m | 714 ------------------ VoidLink.xcodeproj/project.pbxproj | 148 ++++ VoidLink/Database/DataManager.h | 7 +- VoidLink/Database/DataManager.m | 13 +- VoidLink/Database/TemporarySettings.h | 4 + VoidLink/ImGuiRenderer 2.h | 45 ++ {Limelight => VoidLink}/ImGuiRenderer.h | 0 {Limelight => VoidLink}/ImGuiRenderer.mm | 9 + .../VoidLink v1.0.xcdatamodel/contents | 3 +- {Limelight => VoidLink}/Metal/MetalConfig.h | 9 + .../Metal/MetalVideoRenderer.h | 9 + .../Metal/MetalVideoRenderer.m | 9 + {Limelight => VoidLink}/Metal/MetalView.h | 9 + {Limelight => VoidLink}/Metal/MetalView.m | 9 + .../Metal/MetalViewController.h | 9 + .../Metal/MetalViewController.m | 9 + {Limelight => VoidLink}/Metal/Shaders.metal | 9 + VoidLink/SceneDelegate.h | 12 + VoidLink/SceneDelegate.m | 91 +++ {Limelight => VoidLink}/Stats/ImGuiPlots.h | 9 + {Limelight => VoidLink}/Stats/ImGuiPlots.mm | 0 VoidLink/Stream/StreamManager.h | 2 +- VoidLink/Stream/StreamManager.m | 19 +- .../ViewControllers/SettingsViewController.h | 8 + .../ViewControllers/SettingsViewController.m | 79 +- .../StreamFrameViewController.m | 4 +- moonlight-common/moonlight-common-c | 2 +- third_party/implot | 1 - 36 files changed, 565 insertions(+), 836 deletions(-) create mode 100644 ImGui/imgui_impl_metal.h delete mode 100644 Limelight/Database/DataManager.h delete mode 100644 Limelight/Database/TemporarySettings.h delete mode 100644 Limelight/Input/RelativeTouchHandler.h delete mode 100644 Limelight/Input/RelativeTouchHandler.m delete mode 100644 Limelight/Input/StreamView.h delete mode 100644 Limelight/ViewControllers/StreamFrameViewController.m create mode 100644 VoidLink/ImGuiRenderer 2.h rename {Limelight => VoidLink}/ImGuiRenderer.h (100%) rename {Limelight => VoidLink}/ImGuiRenderer.mm (98%) rename {Limelight => VoidLink}/Metal/MetalConfig.h (80%) rename {Limelight => VoidLink}/Metal/MetalVideoRenderer.h (84%) rename {Limelight => VoidLink}/Metal/MetalVideoRenderer.m (99%) rename {Limelight => VoidLink}/Metal/MetalView.h (85%) rename {Limelight => VoidLink}/Metal/MetalView.m (97%) rename {Limelight => VoidLink}/Metal/MetalViewController.h (80%) rename {Limelight => VoidLink}/Metal/MetalViewController.m (96%) rename {Limelight => VoidLink}/Metal/Shaders.metal (92%) create mode 100644 VoidLink/SceneDelegate.h create mode 100644 VoidLink/SceneDelegate.m rename {Limelight => VoidLink}/Stats/ImGuiPlots.h (83%) rename {Limelight => VoidLink}/Stats/ImGuiPlots.mm (100%) delete mode 160000 third_party/implot diff --git a/.gitmodules b/.gitmodules index 152009bc..094c6182 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "moonlight-common/moonlight-common-c"] path = moonlight-common/moonlight-common-c -url=https://github.com/TrueZhuangJia/voidlink-c.git + url=https://github.com/Acaki/voidlink-c [submodule "X1Kit"] path = X1Kit url = https://github.com/cgutman/X1Kit.git diff --git a/ImGui/imgui_impl_metal.h b/ImGui/imgui_impl_metal.h new file mode 100644 index 00000000..2a9a26a0 --- /dev/null +++ b/ImGui/imgui_impl_metal.h @@ -0,0 +1,78 @@ +// dear imgui: Renderer Backend for Metal +// This needs to be used along with a Platform Backend (e.g. OSX) + +// Implemented features: +// [X] Renderer: User texture binding. Use 'MTLTexture' as texture identifier. Read the FAQ about ImTextureID/ImTextureRef! +// [X] Renderer: Large meshes support (64k+ vertices) even with 16-bit indices (ImGuiBackendFlags_RendererHasVtxOffset). +// [X] Renderer: Texture updates support for dynamic font atlas (ImGuiBackendFlags_RendererHasTextures). + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// Learn about Dear ImGui: +// - FAQ https://dearimgui.com/faq +// - Getting Started https://dearimgui.com/getting-started +// - Documentation https://dearimgui.com/docs (same as your local docs/ folder). +// - Introduction, links and more at the top of imgui.cpp + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API +#ifndef IMGUI_DISABLE + +//----------------------------------------------------------------------------- +// ObjC API +//----------------------------------------------------------------------------- + +#ifdef __OBJC__ + +@class MTLRenderPassDescriptor; +@protocol MTLDevice, MTLCommandBuffer, MTLRenderCommandEncoder; + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplMetal_Init(id device); +IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTLRenderPassDescriptor* renderPassDescriptor); +IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* drawData, + id commandBuffer, + id commandEncoder); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(id device); +IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. +IMGUI_IMPL_API void ImGui_ImplMetal_UpdateTexture(ImTextureData* tex); + +#endif + +//----------------------------------------------------------------------------- +// C++ API +//----------------------------------------------------------------------------- + +// Enable Metal C++ binding support with '#define IMGUI_IMPL_METAL_CPP' in your imconfig.h file +// More info about using Metal from C++: https://developer.apple.com/metal/cpp/ + +#ifdef IMGUI_IMPL_METAL_CPP +#include +#ifndef __OBJC__ + +// Follow "Getting Started" link and check examples/ folder to learn about using backends! +IMGUI_IMPL_API bool ImGui_ImplMetal_Init(MTL::Device* device); +IMGUI_IMPL_API void ImGui_ImplMetal_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplMetal_NewFrame(MTL::RenderPassDescriptor* renderPassDescriptor); +IMGUI_IMPL_API void ImGui_ImplMetal_RenderDrawData(ImDrawData* draw_data, + MTL::CommandBuffer* commandBuffer, + MTL::RenderCommandEncoder* commandEncoder); + +// Called by Init/NewFrame/Shutdown +IMGUI_IMPL_API bool ImGui_ImplMetal_CreateDeviceObjects(MTL::Device* device); +IMGUI_IMPL_API void ImGui_ImplMetal_DestroyDeviceObjects(); + +// (Advanced) Use e.g. if you need to precisely control the timing of texture updates (e.g. for staged rendering), by setting ImDrawData::Textures = NULL to handle this manually. +IMGUI_IMPL_API void ImGui_ImplMetal_UpdateTexture(ImTextureData* tex); + +#endif +#endif + +//----------------------------------------------------------------------------- + +#endif // #ifndef IMGUI_DISABLE diff --git a/Limelight/Database/DataManager.h b/Limelight/Database/DataManager.h deleted file mode 100644 index e69de29b..00000000 diff --git a/Limelight/Database/TemporarySettings.h b/Limelight/Database/TemporarySettings.h deleted file mode 100644 index da35b30c..00000000 --- a/Limelight/Database/TemporarySettings.h +++ /dev/null @@ -1,40 +0,0 @@ -// -// TemporarySettings.h -// Moonlight -// -// Created by Cameron Gutman on 12/1/15. -// Copyright © 2015 Moonlight Stream. All rights reserved. -// - -#import "Settings+CoreDataClass.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 * audioConfig; -@property (nonatomic, retain) NSNumber * onscreenControls; -@property (nonatomic, retain) NSString * uniqueId; -@property (nonatomic) enum { - CODEC_PREF_AUTO, - CODEC_PREF_H264, - CODEC_PREF_HEVC, - CODEC_PREF_AV1, -} preferredCodec; -@property (nonatomic) BOOL useFramePacing; -@property (nonatomic) BOOL multiController; -@property (nonatomic) BOOL swapABXYButtons; -@property (nonatomic) BOOL playAudioOnPC; -@property (nonatomic) BOOL optimizeGames; -@property (nonatomic) BOOL enableHdr; -@property (nonatomic) BOOL btMouseSupport; -@property (nonatomic) BOOL absoluteTouchMode; -@property (nonatomic) BOOL statsOverlay; - -- (id) initFromSettings:(Settings*)settings; - -@end diff --git a/Limelight/Input/RelativeTouchHandler.h b/Limelight/Input/RelativeTouchHandler.h deleted file mode 100644 index e69de29b..00000000 diff --git a/Limelight/Input/RelativeTouchHandler.m b/Limelight/Input/RelativeTouchHandler.m deleted file mode 100644 index e69de29b..00000000 diff --git a/Limelight/Input/StreamView.h b/Limelight/Input/StreamView.h deleted file mode 100644 index 9f500101..00000000 --- a/Limelight/Input/StreamView.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// StreamView.h -// Moonlight -// -// Created by Cameron Gutman on 10/19/14. -// Copyright (c) 2014 Moonlight Stream. All rights reserved. -// - -#import "ControllerSupport.h" -#import "OnScreenControls.h" -#import "Moonlight-Swift.h" -#import "StreamConfiguration.h" - -@protocol UserInteractionDelegate - -- (void) userInteractionBegan; -- (void) userInteractionEnded; - -@end - -#if TARGET_OS_TV -@interface StreamView : UIView -#else -@interface StreamView : UIView -#endif - -- (void) setupStreamView:(ControllerSupport*)controllerSupport - interactionDelegate:(id)interactionDelegate - config:(StreamConfiguration*)streamConfig; -- (void) showOnScreenControls; -- (OnScreenControlsLevel) getCurrentOscState; - -#if !TARGET_OS_TV -- (void) updateCursorLocation:(CGPoint)location isMouse:(BOOL)isMouse; -#endif - -@end diff --git a/Limelight/ViewControllers/SettingsViewController.m b/Limelight/ViewControllers/SettingsViewController.m index 0927d84f..86707a72 100644 --- a/Limelight/ViewControllers/SettingsViewController.m +++ b/Limelight/ViewControllers/SettingsViewController.m @@ -1128,7 +1128,6 @@ BOOL isCustomResolution(CGSize res) { NSInteger touchMode = [self.touchModeSelector selectedSegmentIndex]; NSInteger statsOverlayLevel = [self.statsOverlaySelector selectedSegmentIndex]; BOOL statsOverlayEnabled = statsOverlayLevel != 0; - BOOL statsOverlay = [self.statsOverlaySelector selectedSegmentIndex] == 1; BOOL enableGraphs = [self.enableGraphsSelector selectedSegmentIndex] == 1; BOOL enableHdr = [self.hdrSelector selectedSegmentIndex] == 1; BOOL unlockDisplayOrientation = [self.unlockDisplayOrientationSelector selectedSegmentIndex] == 1; @@ -1172,7 +1171,6 @@ BOOL isCustomResolution(CGSize res) { resolutionSelected:resolutionSelected externalDisplayMode:externalDisplayMode localMousePointerMode:localMousePointerMode]; - statsOverlay:statsOverlay enableGraphs:enableGraphs graphOpacity:_graphOpacity renderingBackend:renderingBackend]; diff --git a/Limelight/ViewControllers/StreamFrameViewController.m b/Limelight/ViewControllers/StreamFrameViewController.m deleted file mode 100644 index a4c8f344..00000000 --- a/Limelight/ViewControllers/StreamFrameViewController.m +++ /dev/null @@ -1,714 +0,0 @@ -// -// StreamFrameViewController.m -// Moonlight -// -// Created by Diego Waxemberg on 1/18/14. -// Copyright (c) 2015 Moonlight Stream. All rights reserved. -// - -#import "StreamFrameViewController.h" -#import "MainFrameViewController.h" -#import "VideoDecoderRenderer.h" -#import "StreamManager.h" -#import "ControllerSupport.h" -#import "DataManager.h" - -#include -#include -#include -#include - -#if TARGET_OS_TV -#import -#import -#import -#endif - -@interface AVDisplayCriteria() -@property(readonly) int videoDynamicRange; -@property(readonly, nonatomic) float refreshRate; -- (id)initWithRefreshRate:(float)arg1 videoDynamicRange:(int)arg2; -@end - -@implementation StreamFrameViewController { - ControllerSupport *_controllerSupport; - StreamManager *_streamMan; - TemporarySettings *_settings; - NSTimer *_inactivityTimer; - NSTimer *_statsUpdateTimer; - UITapGestureRecognizer *_menuTapGestureRecognizer; - UITapGestureRecognizer *_menuDoubleTapGestureRecognizer; - UITapGestureRecognizer *_playPauseTapGestureRecognizer; - UITextView *_overlayView; - UILabel *_stageLabel; - UILabel *_tipLabel; - UIActivityIndicatorView *_spinner; - StreamView *_streamView; - UIScrollView *_scrollView; - BOOL _userIsInteracting; - CGSize _keyboardSize; - -#if !TARGET_OS_TV - UIScreenEdgePanGestureRecognizer *_exitSwipeRecognizer; -#endif -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; - -#if !TARGET_OS_TV - [[self revealViewController] setPrimaryViewController:self]; -#endif -} - -#if TARGET_OS_TV -- (void)controllerPauseButtonPressed:(id)sender { } -- (void)controllerPauseButtonDoublePressed:(id)sender { - Log(LOG_I, @"Menu double-pressed -- backing out of stream"); - [self returnToMainFrame]; -} -- (void)controllerPlayPauseButtonPressed:(id)sender { - Log(LOG_I, @"Play/Pause button pressed -- backing out of stream"); - [self returnToMainFrame]; -} -#endif - - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - [self.navigationController setNavigationBarHidden:YES animated:YES]; - - [UIApplication sharedApplication].idleTimerDisabled = YES; - - _settings = [[[DataManager alloc] init] getSettings]; - - _stageLabel = [[UILabel alloc] init]; - [_stageLabel setUserInteractionEnabled:NO]; - [_stageLabel setText:[NSString stringWithFormat:@"Starting %@...", self.streamConfig.appName]]; - [_stageLabel sizeToFit]; - _stageLabel.textAlignment = NSTextAlignmentCenter; - _stageLabel.textColor = [UIColor whiteColor]; - _stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2); - - _spinner = [[UIActivityIndicatorView alloc] init]; - [_spinner setUserInteractionEnabled:NO]; -#if TARGET_OS_TV - [_spinner setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhiteLarge]; -#else - [_spinner setActivityIndicatorViewStyle:UIActivityIndicatorViewStyleWhite]; -#endif - [_spinner sizeToFit]; - [_spinner startAnimating]; - _spinner.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height / 2 - _stageLabel.frame.size.height - _spinner.frame.size.height); - - _controllerSupport = [[ControllerSupport alloc] initWithConfig:self.streamConfig delegate:self]; - _inactivityTimer = nil; - - _streamView = [[StreamView alloc] initWithFrame:self.view.frame]; - [_streamView setupStreamView:_controllerSupport interactionDelegate:self config:self.streamConfig]; - -#if TARGET_OS_TV - if (!_menuTapGestureRecognizer || !_menuDoubleTapGestureRecognizer || !_playPauseTapGestureRecognizer) { - _menuTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(controllerPauseButtonPressed:)]; - _menuTapGestureRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)]; - - _playPauseTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(controllerPlayPauseButtonPressed:)]; - _playPauseTapGestureRecognizer.allowedPressTypes = @[@(UIPressTypePlayPause)]; - - _menuDoubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(controllerPauseButtonDoublePressed:)]; - _menuDoubleTapGestureRecognizer.numberOfTapsRequired = 2; - [_menuTapGestureRecognizer requireGestureRecognizerToFail:_menuDoubleTapGestureRecognizer]; - _menuDoubleTapGestureRecognizer.allowedPressTypes = @[@(UIPressTypeMenu)]; - } - - [self.view addGestureRecognizer:_menuTapGestureRecognizer]; - [self.view addGestureRecognizer:_menuDoubleTapGestureRecognizer]; - [self.view addGestureRecognizer:_playPauseTapGestureRecognizer]; - -#else - _exitSwipeRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(edgeSwiped)]; - _exitSwipeRecognizer.edges = UIRectEdgeLeft; - _exitSwipeRecognizer.delaysTouchesBegan = NO; - _exitSwipeRecognizer.delaysTouchesEnded = NO; - - [self.view addGestureRecognizer:_exitSwipeRecognizer]; -#endif - - _tipLabel = [[UILabel alloc] init]; - [_tipLabel setUserInteractionEnabled:NO]; - -#if TARGET_OS_TV - [_tipLabel setText:@"Tip: Tap the Play/Pause button on the Apple TV Remote to disconnect from your PC"]; -#else - [_tipLabel setText:@"Tip: Swipe from the left edge to disconnect from your PC"]; -#endif - - [_tipLabel sizeToFit]; - _tipLabel.textColor = [UIColor whiteColor]; - _tipLabel.textAlignment = NSTextAlignmentCenter; - _tipLabel.center = CGPointMake(self.view.frame.size.width / 2, self.view.frame.size.height * 0.9); - - _streamMan = [[StreamManager alloc] initWithConfig:self.streamConfig - renderView:_streamView - connectionCallbacks:self]; - NSOperationQueue* opQueue = [[NSOperationQueue alloc] init]; - [opQueue addOperation:_streamMan]; - - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(applicationWillResignActive:) - name:UIApplicationWillResignActiveNotification - object:nil]; - - [[NSNotificationCenter defaultCenter] addObserver: self - selector: @selector(applicationDidBecomeActive:) - name: UIApplicationDidBecomeActiveNotification - object: nil]; - - [[NSNotificationCenter defaultCenter] addObserver: self - selector: @selector(applicationDidEnterBackground:) - name: UIApplicationDidEnterBackgroundNotification - object: nil]; - -#if 0 - // FIXME: This doesn't work reliably on iPad for some reason. Showing and hiding the keyboard - // several times in a row will not correctly restore the state of the UIScrollView. - [[NSNotificationCenter defaultCenter] addObserver: self - selector: @selector(keyboardWillShow:) - name: UIKeyboardWillShowNotification - object: nil]; - - [[NSNotificationCenter defaultCenter] addObserver: self - selector: @selector(keyboardWillHide:) - name: UIKeyboardWillHideNotification - object: nil]; -#endif - - // Only enable scroll and zoom in absolute touch mode - if (_settings.absoluteTouchMode) { - _scrollView = [[UIScrollView alloc] initWithFrame:self.view.frame]; -#if !TARGET_OS_TV - [_scrollView.panGestureRecognizer setMinimumNumberOfTouches:2]; -#endif - [_scrollView setShowsHorizontalScrollIndicator:NO]; - [_scrollView setShowsVerticalScrollIndicator:NO]; - [_scrollView setDelegate:self]; - [_scrollView setMaximumZoomScale:10.0f]; - - // Add StreamView inside a UIScrollView for absolute mode - [_scrollView addSubview:_streamView]; - [self.view addSubview:_scrollView]; - } - else { - // Add StreamView directly in relative mode - [self.view addSubview:_streamView]; - } - - [self.view addSubview:_stageLabel]; - [self.view addSubview:_spinner]; - [self.view addSubview:_tipLabel]; -} - -- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { - return _streamView; -} - -- (void)willMoveToParentViewController:(UIViewController *)parent { - // Only cleanup when we're being destroyed - if (parent == nil) { - [_controllerSupport cleanup]; - [UIApplication sharedApplication].idleTimerDisabled = NO; - [_streamMan stopStream]; - if (_inactivityTimer != nil) { - [_inactivityTimer invalidate]; - _inactivityTimer = nil; - } - [[NSNotificationCenter defaultCenter] removeObserver:self]; - } -} - -#if 0 -- (void)keyboardWillShow:(NSNotification *)notification { - _keyboardSize = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; - - [UIView animateWithDuration:0.3 animations:^{ - CGRect frame = self->_scrollView.frame; - frame.size.height -= self->_keyboardSize.height; - self->_scrollView.frame = frame; - }]; -} - --(void)keyboardWillHide:(NSNotification *)notification { - // NOTE: UIKeyboardFrameEndUserInfoKey returns a different keyboard size - // than UIKeyboardFrameBeginUserInfoKey, so it's unsuitable for use here - // to undo the changes made by keyboardWillShow. - - [UIView animateWithDuration:0.3 animations:^{ - CGRect frame = self->_scrollView.frame; - frame.size.height += self->_keyboardSize.height; - self->_scrollView.frame = frame; - }]; -} -#endif - -- (void)updateStatsOverlay { - NSString* overlayText = [self->_streamMan getStatsOverlayText]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [self updateOverlayText:overlayText]; - }); -} - -- (void)updateOverlayText:(NSString*)text { - if (_overlayView == nil) { - _overlayView = [[UITextView alloc] init]; -#if !TARGET_OS_TV - [_overlayView setEditable:NO]; -#endif - [_overlayView setUserInteractionEnabled:NO]; - [_overlayView setSelectable:NO]; - [_overlayView setScrollEnabled:NO]; - - // HACK: If not using stats overlay, center the text - if (_statsUpdateTimer == nil) { - [_overlayView setTextAlignment:NSTextAlignmentCenter]; - } - - [_overlayView setTextColor:[UIColor lightGrayColor]]; - [_overlayView setBackgroundColor:[UIColor blackColor]]; -#if TARGET_OS_TV - [_overlayView setFont:[UIFont systemFontOfSize:24]]; -#else - [_overlayView setFont:[UIFont systemFontOfSize:12]]; -#endif - [_overlayView setAlpha:0.5]; - [self.view addSubview:_overlayView]; - } - - if (text != nil) { - // We set our bounds to the maximum width in order to work around a bug where - // sizeToFit interacts badly with the UITextView's line breaks, causing the - // width to get smaller and smaller each time as more line breaks are inserted. - [_overlayView setBounds:CGRectMake(self.view.frame.origin.x, - _overlayView.frame.origin.y, - self.view.frame.size.width, - _overlayView.frame.size.height)]; - [_overlayView setText:text]; - [_overlayView sizeToFit]; - [_overlayView setCenter:CGPointMake(self.view.frame.size.width / 2, _overlayView.frame.size.height / 2)]; - [_overlayView setHidden:NO]; - } - else { - [_overlayView setHidden:YES]; - } -} - -- (void) returnToMainFrame { - // Reset display mode back to default - [self updatePreferredDisplayMode:NO]; - - [_statsUpdateTimer invalidate]; - _statsUpdateTimer = nil; - - [self.navigationController popToRootViewControllerAnimated:YES]; -} - -// This will fire if the user opens control center or gets a low battery message -- (void)applicationWillResignActive:(NSNotification *)notification { - if (_inactivityTimer != nil) { - [_inactivityTimer invalidate]; - } - -#if !TARGET_OS_TV - // Terminate the stream if the app is inactive for 60 seconds - Log(LOG_I, @"Starting inactivity termination timer"); - _inactivityTimer = [NSTimer scheduledTimerWithTimeInterval:60 - target:self - selector:@selector(inactiveTimerExpired:) - userInfo:nil - repeats:NO]; -#endif -} - -- (void)inactiveTimerExpired:(NSTimer*)timer { - Log(LOG_I, @"Terminating stream after inactivity"); - - [self returnToMainFrame]; - - _inactivityTimer = nil; -} - -- (void)applicationDidBecomeActive:(NSNotification *)notification { - // Stop the background timer, since we're foregrounded again - if (_inactivityTimer != nil) { - Log(LOG_I, @"Stopping inactivity timer after becoming active again"); - [_inactivityTimer invalidate]; - _inactivityTimer = nil; - } -} - -// This fires when the home button is pressed -- (void)applicationDidEnterBackground:(UIApplication *)application { - Log(LOG_I, @"Terminating stream immediately for backgrounding"); - - if (_inactivityTimer != nil) { - [_inactivityTimer invalidate]; - _inactivityTimer = nil; - } - - [self returnToMainFrame]; -} - -- (void)edgeSwiped { - Log(LOG_I, @"User swiped to end stream"); - - [self returnToMainFrame]; -} - -- (void) connectionStarted { - Log(LOG_I, @"Connection started"); - dispatch_async(dispatch_get_main_queue(), ^{ - // Leave the spinner spinning until it's obscured by - // the first frame of video. - self->_stageLabel.hidden = YES; - self->_tipLabel.hidden = YES; - - [self->_streamView showOnScreenControls]; - - [self->_controllerSupport connectionEstablished]; - - if (self->_settings.statsOverlay) { - self->_statsUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f - target:self - selector:@selector(updateStatsOverlay) - userInfo:nil - repeats:YES]; - } - }); -} - -- (void)connectionTerminated:(int)errorCode { - Log(LOG_I, @"Connection terminated: %d", errorCode); - - unsigned int portFlags = LiGetPortFlagsFromTerminationErrorCode(errorCode); - unsigned int portTestResults = LiTestClientConnectivity(CONN_TEST_SERVER, 443, portFlags); - - dispatch_async(dispatch_get_main_queue(), ^{ - // Allow the display to go to sleep now - [UIApplication sharedApplication].idleTimerDisabled = NO; - - NSString* title; - NSString* message; - - if (portTestResults != ML_TEST_RESULT_INCONCLUSIVE && portTestResults != 0) { - title = @"Connection Error"; - message = @"Your device's network connection is blocking Moonlight. Streaming may not work while connected to this network."; - } - else { - switch (errorCode) { - case ML_ERROR_GRACEFUL_TERMINATION: - [self returnToMainFrame]; - return; - - case ML_ERROR_NO_VIDEO_TRAFFIC: - title = @"Connection Error"; - message = @"No video received from host."; - if (portFlags != 0) { - char failingPorts[256]; - LiStringifyPortFlags(portFlags, "\n", failingPorts, sizeof(failingPorts)); - message = [message stringByAppendingString:[NSString stringWithFormat:@"\n\nCheck your firewall and port forwarding rules for port(s):\n%s", failingPorts]]; - } - break; - - case ML_ERROR_NO_VIDEO_FRAME: - title = @"Connection Error"; - message = @"Your network connection isn't performing well. Reduce your video bitrate setting or try a faster connection."; - break; - - case ML_ERROR_UNEXPECTED_EARLY_TERMINATION: - case ML_ERROR_PROTECTED_CONTENT: - title = @"Connection Error"; - message = @"Something went wrong on your host PC when starting the stream.\n\nMake sure you don't have any DRM-protected content open on your host PC. You can also try restarting your host PC.\n\nIf the issue persists, try reinstalling your GPU drivers and GeForce Experience."; - break; - - case ML_ERROR_FRAME_CONVERSION: - title = @"Connection Error"; - message = @"The host PC reported a fatal video encoding error.\n\nTry disabling HDR mode, changing the streaming resolution, or changing your host PC's display resolution."; - break; - - default: - { - NSString* errorString; - if (abs(errorCode) > 1000) { - // We'll assume large errors are hex values - errorString = [NSString stringWithFormat:@"%08X", (uint32_t)errorCode]; - } - else { - // Smaller values will just be printed as decimal (probably errno.h values) - errorString = [NSString stringWithFormat:@"%d", errorCode]; - } - - title = @"Connection Terminated"; - message = [NSString stringWithFormat: @"The connection was terminated\n\nError code: %@", errorString]; - break; - } - } - } - - UIAlertController* conTermAlert = [UIAlertController alertControllerWithTitle:title - message:message - preferredStyle:UIAlertControllerStyleAlert]; - [Utils addHelpOptionToDialog:conTermAlert]; - [conTermAlert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - [self returnToMainFrame]; - }]]; - [self presentViewController:conTermAlert animated:YES completion:nil]; - }); - - [_streamMan stopStream]; -} - -- (void) stageStarting:(const char*)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]]; - [self->_stageLabel setText:titleCase]; - [self->_stageLabel sizeToFit]; - self->_stageLabel.center = CGPointMake(self.view.frame.size.width / 2, self->_stageLabel.center.y); - }); -} - -- (void) stageComplete:(const char*)stageName { -} - -- (void) stageFailed:(const char*)stageName withError:(int)errorCode portTestFlags:(int)portTestFlags { - Log(LOG_I, @"Stage %s failed: %d", stageName, errorCode); - - unsigned int portTestResults = LiTestClientConnectivity(CONN_TEST_SERVER, 443, portTestFlags); - - dispatch_async(dispatch_get_main_queue(), ^{ - // Allow the display to go to sleep now - [UIApplication sharedApplication].idleTimerDisabled = NO; - - NSString* message = [NSString stringWithFormat:@"%s failed with error %d", stageName, errorCode]; - if (portTestFlags != 0) { - char failingPorts[256]; - LiStringifyPortFlags(portTestFlags, "\n", failingPorts, sizeof(failingPorts)); - message = [message stringByAppendingString:[NSString stringWithFormat:@"\n\nCheck your firewall and port forwarding rules for port(s):\n%s", failingPorts]]; - } - if (portTestResults != ML_TEST_RESULT_INCONCLUSIVE && portTestResults != 0) { - message = [message stringByAppendingString:@"\n\nYour device's network connection is blocking Moonlight. Streaming may not work while connected to this network."]; - } - - UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Failed" - message:message - preferredStyle:UIAlertControllerStyleAlert]; - [Utils addHelpOptionToDialog:alert]; - [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - [self returnToMainFrame]; - }]]; - [self presentViewController:alert animated:YES completion:nil]; - }); - - [_streamMan stopStream]; -} - -- (void) launchFailed:(NSString*)message { - Log(LOG_I, @"Launch failed: %@", message); - - dispatch_async(dispatch_get_main_queue(), ^{ - // Allow the display to go to sleep now - [UIApplication sharedApplication].idleTimerDisabled = NO; - - UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Connection Error" - message:message - preferredStyle:UIAlertControllerStyleAlert]; - [Utils addHelpOptionToDialog:alert]; - [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){ - [self returnToMainFrame]; - }]]; - [self presentViewController:alert animated:YES completion:nil]; - }); -} - -- (void)rumble:(unsigned short)controllerNumber lowFreqMotor:(unsigned short)lowFreqMotor highFreqMotor:(unsigned short)highFreqMotor { - Log(LOG_I, @"Rumble on gamepad %d: %04x %04x", controllerNumber, lowFreqMotor, highFreqMotor); - - [_controllerSupport rumble:controllerNumber lowFreqMotor:lowFreqMotor highFreqMotor:highFreqMotor]; -} - -- (void) rumbleTriggers:(uint16_t)controllerNumber leftTrigger:(uint16_t)leftTrigger rightTrigger:(uint16_t)rightTrigger { - Log(LOG_I, @"Trigger rumble on gamepad %d: %04x %04x", controllerNumber, leftTrigger, rightTrigger); - - [_controllerSupport rumbleTriggers:controllerNumber leftTrigger:leftTrigger rightTrigger:rightTrigger]; -} - -- (void) setMotionEventState:(uint16_t)controllerNumber motionType:(uint8_t)motionType reportRateHz:(uint16_t)reportRateHz { - Log(LOG_I, @"Set motion state on gamepad %d: %02x %u Hz", controllerNumber, motionType, reportRateHz); - - [_controllerSupport setMotionEventState:controllerNumber motionType:motionType reportRateHz:reportRateHz]; -} - -- (void) setControllerLed:(uint16_t)controllerNumber r:(uint8_t)r g:(uint8_t)g b:(uint8_t)b { - Log(LOG_I, @"Set controller LED on gamepad %d: l%02x%02x%02x", controllerNumber, r, g, b); - - [_controllerSupport setControllerLed:controllerNumber r:r g:g b:b]; -} - -- (void)connectionStatusUpdate:(int)status { - Log(LOG_W, @"Connection status update: %d", status); - - // The stats overlay takes precedence over these warnings - if (_statsUpdateTimer != nil) { - return; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - switch (status) { - case CONN_STATUS_OKAY: - [self updateOverlayText:nil]; - break; - - case CONN_STATUS_POOR: - if (self->_streamConfig.bitRate > 5000) { - [self updateOverlayText:@"Slow connection to PC\nReduce your bitrate"]; - } - else { - [self updateOverlayText:@"Poor connection to PC"]; - } - break; - } - }); -} - -- (void) updatePreferredDisplayMode:(BOOL)streamActive { -#if TARGET_OS_TV - if (@available(tvOS 11.2, *)) { - UIWindow* window = [[[UIApplication sharedApplication] delegate] window]; - AVDisplayManager* displayManager = [window avDisplayManager]; - - // This logic comes from Kodi and MrMC - if (streamActive) { - int dynamicRange; - - if (LiGetCurrentHostDisplayHdrMode()) { - dynamicRange = 2; // HDR10 - } - else { - dynamicRange = 0; // SDR - } - - AVDisplayCriteria* displayCriteria = [[AVDisplayCriteria alloc] initWithRefreshRate:[_settings.framerate floatValue] - videoDynamicRange:dynamicRange]; - displayManager.preferredDisplayCriteria = displayCriteria; - } - else { - // Switch back to the default display mode - displayManager.preferredDisplayCriteria = nil; - } - } -#endif -} - -- (void) setHdrMode:(bool)enabled { - Log(LOG_I, @"HDR is now: %s", enabled ? "active" : "inactive"); - dispatch_async(dispatch_get_main_queue(), ^{ - [self updatePreferredDisplayMode:YES]; - }); -} - -- (void) videoContentShown { - [_spinner stopAnimating]; - [self.view setBackgroundColor:[UIColor blackColor]]; -} - -- (void)didReceiveMemoryWarning -{ - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -- (void)gamepadPresenceChanged { -#if !TARGET_OS_TV - if (@available(iOS 11.0, *)) { - [self setNeedsUpdateOfHomeIndicatorAutoHidden]; - } -#endif -} - -- (void)mousePresenceChanged { -#if !TARGET_OS_TV - if (@available(iOS 14.0, *)) { - [self setNeedsUpdateOfPrefersPointerLocked]; - } -#endif -} - -- (void) streamExitRequested { - Log(LOG_I, @"Gamepad combo requested stream exit"); - - [self returnToMainFrame]; -} - -- (void)userInteractionBegan { - // Disable hiding home bar when user is interacting. - // iOS will force it to be shown anyway, but it will - // also discard our edges deferring system gestures unless - // we willingly give up home bar hiding preference. - _userIsInteracting = YES; -#if !TARGET_OS_TV - if (@available(iOS 11.0, *)) { - [self setNeedsUpdateOfHomeIndicatorAutoHidden]; - } -#endif -} - -- (void)userInteractionEnded { - // Enable home bar hiding again if conditions allow - _userIsInteracting = NO; -#if !TARGET_OS_TV - if (@available(iOS 11.0, *)) { - [self setNeedsUpdateOfHomeIndicatorAutoHidden]; - } -#endif -} - -#if !TARGET_OS_TV -// Require a confirmation when streaming to activate a system gesture -- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { - return UIRectEdgeAll; -} - -- (BOOL)prefersHomeIndicatorAutoHidden { - if ([_controllerSupport getConnectedGamepadCount] > 0 && - [_streamView getCurrentOscState] == OnScreenControlsLevelOff && - _userIsInteracting == NO) { - // Autohide the home bar when a gamepad is connected - // and the on-screen controls are disabled. We can't - // do this all the time because any touch on the display - // will cause the home indicator to reappear, and our - // preferredScreenEdgesDeferringSystemGestures will also - // be suppressed (leading to possible errant exits of the - // stream). - return YES; - } - - return NO; -} - -- (BOOL)shouldAutorotate { - return YES; -} - -- (BOOL)prefersPointerLocked { - // Pointer lock breaks the UIKit mouse APIs, which is a problem because - // GCMouse is horribly broken on iOS 14.0 for certain mice. Only lock - // the cursor if there is a GCMouse present. - return [GCMouse mice].count > 0; -} -#endif - -@end diff --git a/VoidLink.xcodeproj/project.pbxproj b/VoidLink.xcodeproj/project.pbxproj index c55fa126..cd9aab84 100644 --- a/VoidLink.xcodeproj/project.pbxproj +++ b/VoidLink.xcodeproj/project.pbxproj @@ -37,6 +37,25 @@ 84E1CDFA2C4FE83400547904 /* SwiftLocalizationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84E1CDF92C4FE83400547904 /* SwiftLocalizationHelper.swift */; }; 84FD0B512C31338D006B5BE5 /* LocalizationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 84FD0B502C31338D006B5BE5 /* LocalizationHelper.m */; }; 84FD0B542C3134A6006B5BE5 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 84FD0B532C3134A6006B5BE5 /* Localizable.xcstrings */; }; + 97E20DBF2E21637B000D5DCA /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DBE2E21637B000D5DCA /* SceneDelegate.m */; }; + 97E20DCF2E21647A000D5DCA /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DCD2E21647A000D5DCA /* Shaders.metal */; }; + 97E20DD02E21647A000D5DCA /* MetalViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DCC2E21647A000D5DCA /* MetalViewController.m */; }; + 97E20DD12E21647A000D5DCA /* MetalView.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DCA2E21647A000D5DCA /* MetalView.m */; }; + 97E20DD22E21647A000D5DCA /* MetalVideoRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DC82E21647A000D5DCA /* MetalVideoRenderer.m */; }; + 97E20DDF2E2164A9000D5DCA /* Frame.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DD92E2164A9000D5DCA /* Frame.m */; }; + 97E20DE02E2164A9000D5DCA /* FrameQueue.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DDB2E2164A9000D5DCA /* FrameQueue.m */; }; + 97E20DE12E2164A9000D5DCA /* FloatBuffer.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DD72E2164A9000D5DCA /* FloatBuffer.m */; }; + 97E20DE22E2164A9000D5DCA /* PaddedLabel.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DDD2E2164A9000D5DCA /* PaddedLabel.m */; }; + 97E20DE32E2164A9000D5DCA /* BandwidthTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DD52E2164A9000D5DCA /* BandwidthTracker.m */; }; + 97E20DE72E216510000D5DCA /* ImGuiRenderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DE62E216510000D5DCA /* ImGuiRenderer.mm */; }; + 97E20DEA2E216532000D5DCA /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DE92E216532000D5DCA /* SceneDelegate.m */; }; + 97E20DEE2E2165A5000D5DCA /* ImGuiPlots.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97E20DED2E2165A5000D5DCA /* ImGuiPlots.mm */; }; + 97E21BB52E2186A3000D5DCA /* imgui_impl_metal.mm in Sources */ = {isa = PBXBuildFile; fileRef = 97E21BB32E2186A3000D5DCA /* imgui_impl_metal.mm */; }; + 97E21BBF2E21882D000D5DCA /* imgui_tables.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97E21BBD2E21882D000D5DCA /* imgui_tables.cpp */; }; + 97E21BC02E21882D000D5DCA /* imgui_demo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97E21BBA2E21882D000D5DCA /* imgui_demo.cpp */; }; + 97E21BC12E21882D000D5DCA /* imgui_widgets.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97E21BBE2E21882D000D5DCA /* imgui_widgets.cpp */; }; + 97E21BC22E21882D000D5DCA /* imgui.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97E21BB92E21882D000D5DCA /* imgui.cpp */; }; + 97E21BC32E21882D000D5DCA /* imgui_draw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 97E21BBB2E21882D000D5DCA /* imgui_draw.cpp */; }; 98181BE92791277400E43572 /* libSDL2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 98181BE82791275D00E43572 /* libSDL2.a */; }; 98181BED2791281100E43572 /* CoreMotion.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 98181BEC2791281100E43572 /* CoreMotion.framework */; }; 9819CC14254F107A008A7C8E /* RelativeTouchHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 9819CC13254F107A008A7C8E /* RelativeTouchHandler.m */; }; @@ -185,6 +204,43 @@ 84FD0B4F2C313343006B5BE5 /* LocalizationHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LocalizationHelper.h; sourceTree = ""; }; 84FD0B502C31338D006B5BE5 /* LocalizationHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocalizationHelper.m; sourceTree = ""; }; 84FD0B532C3134A6006B5BE5 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + 97E20DBD2E21637B000D5DCA /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 97E20DBE2E21637B000D5DCA /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + 97E20DC62E21647A000D5DCA /* MetalConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalConfig.h; sourceTree = ""; }; + 97E20DC72E21647A000D5DCA /* MetalVideoRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalVideoRenderer.h; sourceTree = ""; }; + 97E20DC82E21647A000D5DCA /* MetalVideoRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetalVideoRenderer.m; sourceTree = ""; }; + 97E20DC92E21647A000D5DCA /* MetalView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalView.h; sourceTree = ""; }; + 97E20DCA2E21647A000D5DCA /* MetalView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetalView.m; sourceTree = ""; }; + 97E20DCB2E21647A000D5DCA /* MetalViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MetalViewController.h; sourceTree = ""; }; + 97E20DCC2E21647A000D5DCA /* MetalViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MetalViewController.m; sourceTree = ""; }; + 97E20DCD2E21647A000D5DCA /* Shaders.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; + 97E20DD42E2164A9000D5DCA /* BandwidthTracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BandwidthTracker.h; sourceTree = ""; }; + 97E20DD52E2164A9000D5DCA /* BandwidthTracker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BandwidthTracker.m; sourceTree = ""; }; + 97E20DD62E2164A9000D5DCA /* FloatBuffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FloatBuffer.h; sourceTree = ""; }; + 97E20DD72E2164A9000D5DCA /* FloatBuffer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FloatBuffer.m; sourceTree = ""; }; + 97E20DD82E2164A9000D5DCA /* Frame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Frame.h; sourceTree = ""; }; + 97E20DD92E2164A9000D5DCA /* Frame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Frame.m; sourceTree = ""; }; + 97E20DDA2E2164A9000D5DCA /* FrameQueue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FrameQueue.h; sourceTree = ""; }; + 97E20DDB2E2164A9000D5DCA /* FrameQueue.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FrameQueue.m; sourceTree = ""; }; + 97E20DDC2E2164A9000D5DCA /* PaddedLabel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PaddedLabel.h; sourceTree = ""; }; + 97E20DDD2E2164A9000D5DCA /* PaddedLabel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PaddedLabel.m; sourceTree = ""; }; + 97E20DDE2E2164A9000D5DCA /* Plot.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Plot.h; sourceTree = ""; }; + 97E20DE52E2164EE000D5DCA /* ImGuiRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImGuiRenderer.h; sourceTree = ""; }; + 97E20DE62E216510000D5DCA /* ImGuiRenderer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ImGuiRenderer.mm; sourceTree = ""; }; + 97E20DE82E216532000D5DCA /* SceneDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SceneDelegate.h; sourceTree = ""; }; + 97E20DE92E216532000D5DCA /* SceneDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SceneDelegate.m; sourceTree = ""; }; + 97E20DEC2E2165A0000D5DCA /* ImGuiPlots.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ImGuiPlots.h; sourceTree = ""; }; + 97E20DED2E2165A5000D5DCA /* ImGuiPlots.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ImGuiPlots.mm; sourceTree = ""; }; + 97E21BB32E2186A3000D5DCA /* imgui_impl_metal.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = imgui_impl_metal.mm; sourceTree = ""; }; + 97E21BB72E21882D000D5DCA /* imconfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = imconfig.h; path = third_party/imgui/imconfig.h; sourceTree = SOURCE_ROOT; }; + 97E21BB82E21882D000D5DCA /* imgui.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = imgui.h; path = third_party/imgui/imgui.h; sourceTree = SOURCE_ROOT; }; + 97E21BB92E21882D000D5DCA /* imgui.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = imgui.cpp; path = third_party/imgui/imgui.cpp; sourceTree = SOURCE_ROOT; }; + 97E21BBA2E21882D000D5DCA /* imgui_demo.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_demo.cpp; path = third_party/imgui/imgui_demo.cpp; sourceTree = SOURCE_ROOT; }; + 97E21BBB2E21882D000D5DCA /* imgui_draw.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_draw.cpp; path = third_party/imgui/imgui_draw.cpp; sourceTree = SOURCE_ROOT; }; + 97E21BBC2E21882D000D5DCA /* imgui_internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = imgui_internal.h; path = third_party/imgui/imgui_internal.h; sourceTree = SOURCE_ROOT; }; + 97E21BBD2E21882D000D5DCA /* imgui_tables.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_tables.cpp; path = third_party/imgui/imgui_tables.cpp; sourceTree = SOURCE_ROOT; }; + 97E21BBE2E21882D000D5DCA /* imgui_widgets.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = imgui_widgets.cpp; path = third_party/imgui/imgui_widgets.cpp; sourceTree = SOURCE_ROOT; }; + 97E21BC42E21883A000D5DCA /* imgui_impl_metal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = imgui_impl_metal.h; sourceTree = ""; }; 9803CCAB254F9EAF00EE185E /* ConnectionCallbacks.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ConnectionCallbacks.h; sourceTree = ""; }; 98181BE82791275D00E43572 /* libSDL2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSDL2.a; path = libs/SDL2/lib/iOS/libSDL2.a; sourceTree = ""; }; 98181BEA2791278300E43572 /* libSDL2.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libSDL2.a; path = libs/SDL2/lib/tvOS/libSDL2.a; sourceTree = ""; }; @@ -403,6 +459,47 @@ path = Localization; sourceTree = ""; }; + 97E20DCE2E21647A000D5DCA /* Metal */ = { + isa = PBXGroup; + children = ( + 97E20DC62E21647A000D5DCA /* MetalConfig.h */, + 97E20DC72E21647A000D5DCA /* MetalVideoRenderer.h */, + 97E20DC82E21647A000D5DCA /* MetalVideoRenderer.m */, + 97E20DC92E21647A000D5DCA /* MetalView.h */, + 97E20DCA2E21647A000D5DCA /* MetalView.m */, + 97E20DCB2E21647A000D5DCA /* MetalViewController.h */, + 97E20DCC2E21647A000D5DCA /* MetalViewController.m */, + 97E20DCD2E21647A000D5DCA /* Shaders.metal */, + ); + path = Metal; + sourceTree = ""; + }; + 97E20DEB2E21654B000D5DCA /* Stats */ = { + isa = PBXGroup; + children = ( + 97E20DED2E2165A5000D5DCA /* ImGuiPlots.mm */, + 97E20DEC2E2165A0000D5DCA /* ImGuiPlots.h */, + ); + path = Stats; + sourceTree = ""; + }; + 97E21BB42E2186A3000D5DCA /* ImGui */ = { + isa = PBXGroup; + children = ( + 97E21BC42E21883A000D5DCA /* imgui_impl_metal.h */, + 97E21BB72E21882D000D5DCA /* imconfig.h */, + 97E21BB82E21882D000D5DCA /* imgui.h */, + 97E21BB92E21882D000D5DCA /* imgui.cpp */, + 97E21BBA2E21882D000D5DCA /* imgui_demo.cpp */, + 97E21BBB2E21882D000D5DCA /* imgui_draw.cpp */, + 97E21BBC2E21882D000D5DCA /* imgui_internal.h */, + 97E21BBD2E21882D000D5DCA /* imgui_tables.cpp */, + 97E21BBE2E21882D000D5DCA /* imgui_widgets.cpp */, + 97E21BB32E2186A3000D5DCA /* imgui_impl_metal.mm */, + ); + path = ImGui; + sourceTree = ""; + }; 9896219423D15C2900211983 /* X1Kit */ = { isa = PBXGroup; children = ( @@ -435,6 +532,7 @@ FB290CE519B2C406004C83CF = { isa = PBXGroup; children = ( + 97E21BB42E2186A3000D5DCA /* ImGui */, 9896219423D15C2900211983 /* X1Kit */, 98AB2E7F1CAD46830089BB98 /* moonlight-common.xcodeproj */, FB290CF919B2C406004C83CF /* VoidLink */, @@ -482,6 +580,12 @@ FB290CF919B2C406004C83CF /* VoidLink */ = { isa = PBXGroup; children = ( + 97E20DEB2E21654B000D5DCA /* Stats */, + 97E20DE82E216532000D5DCA /* SceneDelegate.h */, + 97E20DE92E216532000D5DCA /* SceneDelegate.m */, + 97E20DCE2E21647A000D5DCA /* Metal */, + 97E20DBD2E21637B000D5DCA /* SceneDelegate.h */, + 97E20DBE2E21637B000D5DCA /* SceneDelegate.m */, 84D8EB932C5146AD00FC10A9 /* VoidLink-Bridging-Header.h */, 84FD0B492C312C0B006B5BE5 /* Localization */, 8454AC682C31907F00F0A302 /* iPad.storyboard */, @@ -516,6 +620,8 @@ FB1D59981BBCCD7E00F482CA /* AppCollectionView.h */, FB1D59991BBCCD7E00F482CA /* AppCollectionView.m */, 843E93BD2C41528700B1651F /* OrientationHelper.swift */, + 97E20DE62E216510000D5DCA /* ImGuiRenderer.mm */, + 97E20DE52E2164EE000D5DCA /* ImGuiRenderer.h */, ); path = VoidLink; sourceTree = ""; @@ -616,6 +722,17 @@ FB89461E19F646E200339C8A /* Utility */ = { isa = PBXGroup; children = ( + 97E20DD42E2164A9000D5DCA /* BandwidthTracker.h */, + 97E20DD52E2164A9000D5DCA /* BandwidthTracker.m */, + 97E20DD62E2164A9000D5DCA /* FloatBuffer.h */, + 97E20DD72E2164A9000D5DCA /* FloatBuffer.m */, + 97E20DD82E2164A9000D5DCA /* Frame.h */, + 97E20DD92E2164A9000D5DCA /* Frame.m */, + 97E20DDA2E2164A9000D5DCA /* FrameQueue.h */, + 97E20DDB2E2164A9000D5DCA /* FrameQueue.m */, + 97E20DDC2E2164A9000D5DCA /* PaddedLabel.h */, + 97E20DDD2E2164A9000D5DCA /* PaddedLabel.m */, + 97E20DDE2E2164A9000D5DCA /* Plot.h */, FB89462119F646E200339C8A /* Utils.h */, FB89462219F646E200339C8A /* Utils.m */, FBD1C8E01A8AD69E00C6703C /* Logger.h */, @@ -859,8 +976,14 @@ 9819CC22254F180F008A7C8E /* AbsoluteTouchHandler.m in Sources */, 9819CC14254F107A008A7C8E /* RelativeTouchHandler.m in Sources */, FB1A674D2131E65900507771 /* KeyboardSupport.m in Sources */, + 97E20DDF2E2164A9000D5DCA /* Frame.m in Sources */, + 97E20DE02E2164A9000D5DCA /* FrameQueue.m in Sources */, + 97E20DE12E2164A9000D5DCA /* FloatBuffer.m in Sources */, + 97E20DE22E2164A9000D5DCA /* PaddedLabel.m in Sources */, + 97E20DE32E2164A9000D5DCA /* BandwidthTracker.m in Sources */, 84591A512BE1625E006AAB10 /* CustomEdgeSlideGestureRecognizer.m in Sources */, 843CC7F62C1E7A1700BBC475 /* NativeTouchHandler.m in Sources */, + 97E20DEE2E2165A5000D5DCA /* ImGuiPlots.mm in Sources */, FB89463219F646E200339C8A /* VideoDecoderRenderer.m in Sources */, FB290D0419B2C406004C83CF /* AppDelegate.m in Sources */, FB9AFD401A7E127D00872C98 /* AppListResponse.m in Sources */, @@ -870,6 +993,11 @@ FB9AFD321A7D867C00872C98 /* AppAssetRetriever.m in Sources */, 843E93BE2C41528700B1651F /* OrientationHelper.swift in Sources */, FB4678FF1A565DAC00377732 /* WakeOnLanManager.m in Sources */, + 97E21BBF2E21882D000D5DCA /* imgui_tables.cpp in Sources */, + 97E21BC02E21882D000D5DCA /* imgui_demo.cpp in Sources */, + 97E21BC12E21882D000D5DCA /* imgui_widgets.cpp in Sources */, + 97E21BC22E21882D000D5DCA /* imgui.cpp in Sources */, + 97E21BC32E21882D000D5DCA /* imgui_draw.cpp in Sources */, 84E1CDEC2C4F67D300547904 /* CommandManager.swift in Sources */, 847D2FDB2DDA015F00937A62 /* MenuSectionView.m in Sources */, FB53E1431BE5DC4400CD6ECE /* IdManager.m in Sources */, @@ -882,7 +1010,9 @@ FB89463019F646E200339C8A /* StreamConfiguration.m in Sources */, FBD3495319FF36FB002D2A60 /* SWRevealViewController.m in Sources */, 84203F232C5F24C00032EEED /* OnScreenWidgetView.swift in Sources */, + 97E21BB52E2186A3000D5DCA /* imgui_impl_metal.mm in Sources */, 84FD0B512C31338D006B5BE5 /* LocalizationHelper.m in Sources */, + 97E20DEA2E216532000D5DCA /* SceneDelegate.m in Sources */, FB1D59971BBCCB6400F482CA /* ComputerScrollView.m in Sources */, FBD3495019FF2174002D2A60 /* SettingsViewController.m in Sources */, 843CC7F02C1E757D00BBC475 /* NativeTouchPointer.m in Sources */, @@ -905,8 +1035,13 @@ 84E1CDEF2C4F688E00547904 /* ToolboxViewController.swift in Sources */, 844CD0EB2C7F8500004861A7 /* PureNativeTouchHandler.m in Sources */, 84351B102C29ACCB00A77989 /* OSCProfile.m in Sources */, + 97E20DCF2E21647A000D5DCA /* Shaders.metal in Sources */, + 97E20DD02E21647A000D5DCA /* MetalViewController.m in Sources */, + 97E20DD12E21647A000D5DCA /* MetalView.m in Sources */, + 97E20DD22E21647A000D5DCA /* MetalVideoRenderer.m in Sources */, 84351B2C2C29AD6100A77989 /* OSCProfilesTableViewController.m in Sources */, 98D585701C0ED0E800F6CC00 /* TemporarySettings.m in Sources */, + 97E20DBF2E21637B000D5DCA /* SceneDelegate.m in Sources */, FB89462819F646E200339C8A /* CryptoManager.m in Sources */, DC1F5A07206436B20037755F /* ConnectionHelper.m in Sources */, FB89462E19F646E200339C8A /* PairManager.m in Sources */, @@ -921,6 +1056,7 @@ FB89462A19F646E200339C8A /* ControllerSupport.m in Sources */, FB9AFD3D1A7E111600872C98 /* AppAssetResponse.m in Sources */, FBD349621A0089F6002D2A60 /* DataManager.m in Sources */, + 97E20DE72E216510000D5DCA /* ImGuiRenderer.mm in Sources */, FB4A23B81A9D3637004D2EF2 /* LoadingFrameViewController.m in Sources */, FB9AFD3A1A7E05CE00872C98 /* ServerInfoResponse.m in Sources */, 84351B2B2C29AD6100A77989 /* LayoutOnScreenControlsViewController.m in Sources */, @@ -1112,6 +1248,7 @@ "$(PROJECT_DIR)/Limelight/ViewControllers/CustomOSCViewControl/Cells", "$(PROJECT_DIR)/Limelight/Input", "$(PROJECT_DIR)/Limelight/Localization", + "$(PROJECT_DIR)/third_party/imgui", "$(PROJECT_DIR)/Limelight/Database", ); INFOPLIST_FILE = "VoidLink/Limelight-Info.plist"; @@ -1122,6 +1259,11 @@ "$(inherited)", "@executable_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/third_party/imgui/examples/libs/glfw/lib-vc2010-32", + "$(PROJECT_DIR)/third_party/imgui/examples/libs/glfw/lib-vc2010-64", + ); "LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = ( "$(inherited)", "$(PROJECT_DIR)/libs/opus/lib/iOS", @@ -1171,6 +1313,7 @@ "$(PROJECT_DIR)/Limelight/ViewControllers/CustomOSCViewControl/Cells", "$(PROJECT_DIR)/Limelight/Input", "$(PROJECT_DIR)/Limelight/Localization", + "$(PROJECT_DIR)/third_party/imgui", "$(PROJECT_DIR)/Limelight/Database", ); INFOPLIST_FILE = "VoidLink/Limelight-Info.plist"; @@ -1181,6 +1324,11 @@ "$(inherited)", "@executable_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/third_party/imgui/examples/libs/glfw/lib-vc2010-32", + "$(PROJECT_DIR)/third_party/imgui/examples/libs/glfw/lib-vc2010-64", + ); "LIBRARY_SEARCH_PATHS[sdk=iphoneos*]" = ( "$(inherited)", "$(PROJECT_DIR)/libs/opus/lib/iOS", diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index 5085f683..d6a5b770 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -78,14 +78,17 @@ typedef NS_ENUM(NSInteger, SettingsMenuMode) { useFramePacing:(BOOL)useFramePacing enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport - // absoluteTouchMode:(BOOL)absoluteTouchMode touchMode:(NSInteger)touchMode statsOverlayLevel:(NSInteger)statsOverlayLevel statsOverlayEnabled:(BOOL)statsOverlayEnabled unlockDisplayOrientation:(BOOL)unlockDisplayOrientation resolutionSelected:(NSInteger)resolutionSelected externalDisplayMode:(NSInteger)externalDisplayMode - localMousePointerMode:(NSInteger)localMousePointerMode; + localMousePointerMode:(NSInteger)localMousePointerMode + frameQueueSize:(NSInteger)frameQueueSize + enableGraphs:(BOOL)enableGraphs + graphOpacity:(NSInteger)graphOpacity + renderingBackend:(NSInteger)renderingBackend; - (NSArray*) getHosts; - (void) updateHost:(TemporaryHost*)host; diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index ab7069d9..be323d40 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -82,20 +82,16 @@ preferredCodec:(uint32_t)preferredCodec enableYUV444:(BOOL)enableYUV444 useFramePacing:(BOOL)useFramePacing - frameQueueSize:(NSInteger)frameQueueSize enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport - // absoluteTouchMode:(BOOL)absoluteTouchMode touchMode:(NSInteger)touchMode statsOverlayLevel:(NSInteger)statsOverlayLevel statsOverlayEnabled:(BOOL)statsOverlayEnabled unlockDisplayOrientation:(BOOL)unlockDisplayOrientation resolutionSelected:(NSInteger)resolutionSelected externalDisplayMode:(NSInteger)externalDisplayMode - localMousePointerMode:(NSInteger)localMousePointerMode{ - - absoluteTouchMode:(BOOL)absoluteTouchMode - statsOverlay:(BOOL)statsOverlay + localMousePointerMode:(NSInteger)localMousePointerMode + frameQueueSize:(NSInteger)frameQueueSize enableGraphs:(BOOL)enableGraphs graphOpacity:(NSInteger)graphOpacity renderingBackend:(NSInteger)renderingBackend @@ -128,7 +124,6 @@ settingsToSave.playAudioOnPC = audioOnPC; settingsToSave.preferredCodec = preferredCodec; settingsToSave.enableYUV444 = enableYUV444; - settingsToSave.frameQueueSize = [NSNumber numberWithInteger:frameQueueSize]; settingsToSave.enableHdr = enableHdr; settingsToSave.btMouseSupport = btMouseSupport; // settingsToSave.absoluteTouchMode = absoluteTouchMode; @@ -139,8 +134,8 @@ settingsToSave.resolutionSelected = [NSNumber numberWithInteger:resolutionSelected]; settingsToSave.externalDisplayMode = [NSNumber numberWithInteger:externalDisplayMode]; settingsToSave.localMousePointerMode = [NSNumber numberWithInteger:localMousePointerMode]; - settingsToSave.absoluteTouchMode = absoluteTouchMode; - settingsToSave.statsOverlay = statsOverlay; + + settingsToSave.frameQueueSize = [NSNumber numberWithInteger:frameQueueSize]; settingsToSave.enableGraphs = enableGraphs; settingsToSave.graphOpacity = [NSNumber numberWithInteger:graphOpacity]; settingsToSave.renderingBackend = [NSNumber numberWithInteger:renderingBackend]; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index 7240d6d5..b7165304 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -62,6 +62,10 @@ @property (nonatomic) BOOL liftStreamViewForKeyboard; @property (nonatomic) BOOL showKeyboardToolbar; @property (nonatomic) BOOL unlockDisplayOrientation; +@property (nonatomic) BOOL enableGraphs; +@property (nonatomic, retain) NSNumber * frameQueueSize; +@property (nonatomic, retain) NSNumber * graphOpacity; +@property (nonatomic, retain) NSNumber * renderingBackend; - (id) initFromSettings:(Settings*)settings; diff --git a/VoidLink/ImGuiRenderer 2.h b/VoidLink/ImGuiRenderer 2.h new file mode 100644 index 00000000..345fd9f7 --- /dev/null +++ b/VoidLink/ImGuiRenderer 2.h @@ -0,0 +1,45 @@ +// +// ImGuiRenderer 2.h +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + +#import +#import +#import +#import + +#import "FloatBuffer.h" +#import "Plot.h" + +typedef void (^MetricsHandler)(int plotId, CFTimeInterval value); + +@interface ImGuiRenderer : UIViewController +@end + +@interface ImGuiRenderer () +@property (nonatomic) CGRect bounds; +@property (nonatomic, readonly) MTKView * _Nonnull mtkView; +@property (nonatomic, strong) id _Nonnull device; +@property (nonatomic, strong) id _Nonnull commandQueue; +@property (nonatomic) struct PlotDef * _Nonnull plots; +@property (nonatomic) FloatBuffer * _Nonnull frametimes; +@property (nonatomic) BOOL enableGraphs; +@property (nonatomic) float graphOpacity; +@property (nonatomic) BOOL imguiRunning; +@property (nonatomic) MetricsHandler _Nonnull metricsHandler; + +-(nonnull instancetype) initWithFrame:(CGRect)bounds + streamFps:(int)streamFps + enableGraphs:(BOOL)enableGraphs + graphOpacity:(int)graphOpacity; +-(void) start; +-(void) show; +-(void) hide; +-(void) stop; +-(void) observeFloat:(int)plotId value:(CFTimeInterval)value; +-(void) observeFloatReturnMetrics:(int)plotId value:(CFTimeInterval)value plotMetrics:(PlotMetrics * _Nullable)plotMetrics; +@end diff --git a/Limelight/ImGuiRenderer.h b/VoidLink/ImGuiRenderer.h similarity index 100% rename from Limelight/ImGuiRenderer.h rename to VoidLink/ImGuiRenderer.h diff --git a/Limelight/ImGuiRenderer.mm b/VoidLink/ImGuiRenderer.mm similarity index 98% rename from Limelight/ImGuiRenderer.mm rename to VoidLink/ImGuiRenderer.mm index 7e22f360..6f4f7738 100644 --- a/Limelight/ImGuiRenderer.mm +++ b/VoidLink/ImGuiRenderer.mm @@ -1,3 +1,12 @@ +// +// ImGuiRenderer.m +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #import #import #import "ImGuiRenderer.h" diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index 9d987176..281f8939 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -54,8 +54,9 @@ + + - diff --git a/Limelight/Metal/MetalConfig.h b/VoidLink/Metal/MetalConfig.h similarity index 80% rename from Limelight/Metal/MetalConfig.h rename to VoidLink/Metal/MetalConfig.h index d107baf6..f21fb332 100644 --- a/Limelight/Metal/MetalConfig.h +++ b/VoidLink/Metal/MetalConfig.h @@ -1,3 +1,12 @@ +// +// MetalConfig.h +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + // When enabled, rendering occurs on the main application thread. // This can make responding to UI events during redraw simpler // to manage because UI calls usually need to occur on the main thread. diff --git a/Limelight/Metal/MetalVideoRenderer.h b/VoidLink/Metal/MetalVideoRenderer.h similarity index 84% rename from Limelight/Metal/MetalVideoRenderer.h rename to VoidLink/Metal/MetalVideoRenderer.h index a5591625..1907734d 100644 --- a/Limelight/Metal/MetalVideoRenderer.h +++ b/VoidLink/Metal/MetalVideoRenderer.h @@ -1,3 +1,12 @@ +// +// MetalVideoRenderer.h +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #import #import #import "ConnectionCallbacks.h" diff --git a/Limelight/Metal/MetalVideoRenderer.m b/VoidLink/Metal/MetalVideoRenderer.m similarity index 99% rename from Limelight/Metal/MetalVideoRenderer.m rename to VoidLink/Metal/MetalVideoRenderer.m index aab8a82c..a9ecd71a 100644 --- a/Limelight/Metal/MetalVideoRenderer.m +++ b/VoidLink/Metal/MetalVideoRenderer.m @@ -1,3 +1,12 @@ +// +// MetalVideoRenderer.m +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #import "MetalVideoRenderer.h" #import #import diff --git a/Limelight/Metal/MetalView.h b/VoidLink/Metal/MetalView.h similarity index 85% rename from Limelight/Metal/MetalView.h rename to VoidLink/Metal/MetalView.h index 603b97ca..b750fd6d 100644 --- a/Limelight/Metal/MetalView.h +++ b/VoidLink/Metal/MetalView.h @@ -1,3 +1,12 @@ +// +// MetalView.h +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #import #import #import diff --git a/Limelight/Metal/MetalView.m b/VoidLink/Metal/MetalView.m similarity index 97% rename from Limelight/Metal/MetalView.m rename to VoidLink/Metal/MetalView.m index d5f4986d..3a104080 100644 --- a/Limelight/Metal/MetalView.m +++ b/VoidLink/Metal/MetalView.m @@ -1,3 +1,12 @@ +// +// MetalView.m +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + // This is based on the following Apple example: // https://developer.apple.com/documentation/metal/achieving-smooth-frame-rates-with-a-metal-display-link?language=objc // https://developer.apple.com/wwdc23/10123/ diff --git a/Limelight/Metal/MetalViewController.h b/VoidLink/Metal/MetalViewController.h similarity index 80% rename from Limelight/Metal/MetalViewController.h rename to VoidLink/Metal/MetalViewController.h index 76903d64..0b283350 100644 --- a/Limelight/Metal/MetalViewController.h +++ b/VoidLink/Metal/MetalViewController.h @@ -1,3 +1,12 @@ +// +// MetalViewController.h +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #import #import "FrameQueue.h" #import "ImGuiRenderer.h" diff --git a/Limelight/Metal/MetalViewController.m b/VoidLink/Metal/MetalViewController.m similarity index 96% rename from Limelight/Metal/MetalViewController.m rename to VoidLink/Metal/MetalViewController.m index 2d4e7888..2446054d 100644 --- a/Limelight/Metal/MetalViewController.m +++ b/VoidLink/Metal/MetalViewController.m @@ -1,3 +1,12 @@ +// +// MetalViewController.m +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + /* See the LICENSE.txt file for this sample’s licensing information. diff --git a/Limelight/Metal/Shaders.metal b/VoidLink/Metal/Shaders.metal similarity index 92% rename from Limelight/Metal/Shaders.metal rename to VoidLink/Metal/Shaders.metal index e65b42a9..c3103732 100644 --- a/Limelight/Metal/Shaders.metal +++ b/VoidLink/Metal/Shaders.metal @@ -1,3 +1,12 @@ +// +// Vertex.swift +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #include #include diff --git a/VoidLink/SceneDelegate.h b/VoidLink/SceneDelegate.h new file mode 100644 index 00000000..f3e37b0c --- /dev/null +++ b/VoidLink/SceneDelegate.h @@ -0,0 +1,12 @@ +#import + +@interface SceneDelegate : UIResponder + +@property (strong, nonatomic) UIWindow * window; + ++ (void)setExternalDisplayRenderView:(UIView *)renderView; ++ (void)clearExternalDisplayRenderView; +- (void)updatePreferredDisplayMode:(BOOL)streamActive withRenderView:(UIView *)renderView; + +@end + diff --git a/VoidLink/SceneDelegate.m b/VoidLink/SceneDelegate.m new file mode 100644 index 00000000..58344cfd --- /dev/null +++ b/VoidLink/SceneDelegate.m @@ -0,0 +1,91 @@ +#import "SceneDelegate.h" +#import "StreamFrameViewController.h" + +@implementation SceneDelegate + +static UIView *_sharedStreamVideoRenderView = nil; +static UIWindow *_externalSceneWindow = nil; + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { + if (![scene isKindOfClass:[UIWindowScene class]]) { + return; + } + UIWindowScene *windowScene = (UIWindowScene *)scene; + if ([session.role isEqualToString:UIWindowSceneSessionRoleApplication]) { + self.window = [[UIWindow alloc] initWithWindowScene:windowScene]; + NSString *storyboardName; + if (UIDevice.currentDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad) { + storyboardName = @"iPad"; + } else { + storyboardName = @"iPhone"; + } + UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil]; + UIViewController *initialViewController = [storyboard instantiateInitialViewController]; + self.window.rootViewController = initialViewController; + [self.window makeKeyAndVisible]; + Log(LOG_I, @"SceneDelegate: Main app scene connected."); + + } else if ([session.role isEqualToString:UIWindowSceneSessionRoleExternalDisplay]) { + Log(LOG_I, @"SceneDelegate: External display scene connecting for screen: %@", ((UIWindowScene *)scene).screen.description); + UIWindowScene *windowScene = (UIWindowScene *)scene; + _externalSceneWindow = [[UIWindow alloc] initWithWindowScene:windowScene]; + UIViewController *externalVC = [[UIViewController alloc] init]; + externalVC.view.backgroundColor = [UIColor blackColor]; // Set a default background + _externalSceneWindow.rootViewController = externalVC; + + if (_sharedStreamVideoRenderView) { + _sharedStreamVideoRenderView.frame = _externalSceneWindow.bounds; + [_externalSceneWindow.rootViewController.view addSubview:_sharedStreamVideoRenderView]; + Log(LOG_I, @"SceneDelegate: External display scene connected."); + } + } +} + + +// Method for StreamFrameViewController to provide its render view ++ (void)setExternalDisplayRenderView:(UIView *)renderView { + _sharedStreamVideoRenderView = renderView; + if (_externalSceneWindow && _externalSceneWindow.rootViewController && _sharedStreamVideoRenderView) { + dispatch_async(dispatch_get_main_queue(), ^{ + // Ensure it's removed from any previous parent (should have been done by StreamFrameVC) + [_sharedStreamVideoRenderView removeFromSuperview]; + _sharedStreamVideoRenderView.frame = _externalSceneWindow.bounds; // Set frame for external window + [_externalSceneWindow.rootViewController.view addSubview:_sharedStreamVideoRenderView]; + _externalSceneWindow.hidden = NO; + Log(LOG_I, @"SceneDelegate: Added render view to external window's root view."); + }); + } else { + Log(LOG_E, @"SceneDelegate: External display window or root view controller not available."); + } +} + ++ (void)clearExternalDisplayRenderView { + if (_sharedStreamVideoRenderView) { + dispatch_async(dispatch_get_main_queue(), ^{ + [_sharedStreamVideoRenderView removeFromSuperview]; + Log(LOG_I, @"SceneDelegate: Removed render view from external display."); + }); + } + _sharedStreamVideoRenderView = nil; +} + +- (void)sceneDidDisconnect:(UIScene *)scene { + Log(LOG_I, @"SceneDelegate: Scene disconnected: %@, role: %@", scene.title, scene.session.role); + + if ([scene.session.role isEqualToString:UIWindowSceneSessionRoleExternalDisplay]) { + if ([scene isKindOfClass:[UIWindowScene class]]) { + UIWindowScene *windowScene = (UIWindowScene *)scene; + if (_externalSceneWindow == windowScene.windows.firstObject) { // Compare with the window from the disconnecting scene + [SceneDelegate clearExternalDisplayRenderView]; // Clears the shared view + _externalSceneWindow = nil; + Log(LOG_I, @"SceneDelegate: External display scene fully disconnected and cleaned up."); + } else { + Log(LOG_W, @"SceneDelegate: Disconnecting scene is not the one holding our _externalSceneWindow."); + } + } else { + Log(LOG_W, @"SceneDelegate: Disconnecting scene is not a UIWindowScene."); + } + } +} + +@end diff --git a/Limelight/Stats/ImGuiPlots.h b/VoidLink/Stats/ImGuiPlots.h similarity index 83% rename from Limelight/Stats/ImGuiPlots.h rename to VoidLink/Stats/ImGuiPlots.h index f94620c6..d293997b 100644 --- a/Limelight/Stats/ImGuiPlots.h +++ b/VoidLink/Stats/ImGuiPlots.h @@ -1,3 +1,12 @@ +// +// ImGuiPlots.h +// VoidLink +// +// Created by Mason Chen on 2025/7/11. +// Copyright © 2025 Moonlight Game Streaming Project. All rights reserved. +// + + #import #import diff --git a/Limelight/Stats/ImGuiPlots.mm b/VoidLink/Stats/ImGuiPlots.mm similarity index 100% rename from Limelight/Stats/ImGuiPlots.mm rename to VoidLink/Stats/ImGuiPlots.mm diff --git a/VoidLink/Stream/StreamManager.h b/VoidLink/Stream/StreamManager.h index 649be215..683f1d31 100644 --- a/VoidLink/Stream/StreamManager.h +++ b/VoidLink/Stream/StreamManager.h @@ -18,6 +18,6 @@ - (void) stopStream; -- (NSString*) getStatsOverlayText: (uint16_t) overlayLevel; +- (NSString*) getStatsOverlayText; @end diff --git a/VoidLink/Stream/StreamManager.m b/VoidLink/Stream/StreamManager.m index 9a85d84f..538bdd33 100644 --- a/VoidLink/Stream/StreamManager.m +++ b/VoidLink/Stream/StreamManager.m @@ -152,11 +152,9 @@ return TRUE; } -- (NSString*) getStatsOverlayText: (uint16_t) overlayLevel { +- (NSString*) getStatsOverlayText { video_stats_t stats; - // NSLog(@"overlayLevel: %d", overlayLevel); - if (!_connection) { return nil; } @@ -168,16 +166,15 @@ uint32_t rtt, variance; NSString* latencyString; if (LiGetEstimatedRttInfo(&rtt, &variance)) { - latencyString = [LocalizationHelper localizedStringForKey:@"%u ms (variance: %u ms)", rtt, variance]; + latencyString = [NSString stringWithFormat:@"%u ms (variance: %u ms)", rtt, variance]; } else { latencyString = @"N/A"; } NSString* hostProcessingString; - // overlayLevel 2: detailed if (stats.framesWithHostProcessingLatency != 0) { - hostProcessingString = [LocalizationHelper localizedStringForKey:@"\nHost processing latency min/max/avg: %.1f/%.1f/%.1f ms", + hostProcessingString = [NSString stringWithFormat:@"Host processing latency min/max/avg: %.1f/%.1f/%.1f ms\n", stats.minHostProcessingLatency / 10.f, stats.maxHostProcessingLatency / 10.f, (float)stats.totalHostProcessingLatency / stats.framesWithHostProcessingLatency / 10.f]; @@ -188,13 +185,12 @@ } float interval = stats.endTime - stats.startTime; - if(overlayLevel == 2) return [LocalizationHelper localizedStringForKey:@"Video stream: %dx%d %.2f FPS (Codec: %@)\nNetwork dropped frames: %.2f%%\nAverage network latency: %@%@", float scalePlotMetrics = stats.frameDropMetrics.nsamples > 0 ? ((float)stats.frameDropMetrics.nsamples / stats.totalFrames) : 1.0f; float fps = (stats.totalFrames - stats.networkDroppedFrames - (stats.frameDropMetrics.total / scalePlotMetrics)) / interval; - + double avgVideoMbps = [_connection getBwTracker].averageMbps; double peakVideoMbps = [_connection getBwTracker].peakMbps; - + return [NSString stringWithFormat:@"Video stream: %dx%d %.2f FPS (Codec: %@)\n" "Bitrate: %.1f Mbps, Peak: %.1f, Renderer: %@\n" "%@" @@ -212,11 +208,6 @@ (stats.networkDroppedFrames / stats.totalFrames) * 100.0, stats.frameDropMetrics.nsamples > 0 ? (stats.frameDropMetrics.total / stats.frameDropMetrics.nsamples) * 100.0 : 0.0f, latencyString, - hostProcessingString]; - else return [LocalizationHelper localizedStringForKey:@"FPS: %5.2f Network dropped frames: %.2f%% Network latency: %@", - stats.totalFrames / interval, - stats.networkDroppedFrames / interval, - latencyString]; stats.decodeMetrics.min, stats.decodeMetrics.max, stats.decodeMetrics.avg]; } diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index 06b09cce..83eda8c3 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -106,6 +106,14 @@ @property (strong, nonatomic) IBOutlet UIStackView *audioConfigStack; @property (strong, nonatomic) IBOutlet UISegmentedControl *audioConfigSelector; +@property (strong, nonatomic) IBOutlet UILabel *frameQueueSizeLabel; +@property (strong, nonatomic) IBOutlet UISlider *frameQueueSizeSlider; + +@property (strong, nonatomic) IBOutlet UILabel *enableGraphsLabel; +@property (strong, nonatomic) IBOutlet UISwitch *enableGraphsSwitch; +@property (strong, nonatomic) IBOutlet UIStepper *graphOpacityStepper; +@property (strong, nonatomic) IBOutlet UISegmentedControl *renderingBackendSelector; + #pragma clang diagnostic push diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index b6c721a9..bb1b1626 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -37,6 +37,8 @@ CGFloat _currentRefreshRate; MenuSectionView *touchControlSection; NSMutableSet* hiddenStacks; + NSInteger _frameQueueSize; + NSInteger _graphOpacity; } @dynamic overrideUserInterfaceStyle; @@ -147,7 +149,7 @@ static const int bitrateTable[] = { 500000, }; -const int RESOLUTION_TABLE_SIZE = 6; +const int RESOLUTION_TABLE_SIZE = 7; const int RESOLUTION_TABLE_CUSTOM_INDEX = RESOLUTION_TABLE_SIZE - 1; CGSize resolutionTable[RESOLUTION_TABLE_SIZE]; @@ -1149,6 +1151,8 @@ BOOL isCustomResolution(CGSize res) { // Ensure we pick a bitrate that falls exactly onto a slider notch _bitrate = bitrateTable[[self getSliderValueForBitrate:[currentSettings.bitrate intValue]]]; + _frameQueueSize = [currentSettings.frameQueueSize intValue]; + _graphOpacity = [currentSettings.graphOpacity intValue]; // Get the size of the screen with and without safe area insets UIWindow *window = UIApplication.sharedApplication.windows.firstObject; @@ -1162,10 +1166,11 @@ BOOL isCustomResolution(CGSize res) { resolutionTable[0] = CGSizeMake(1280, 720); resolutionTable[1] = CGSizeMake(1920, 1080); - resolutionTable[2] = CGSizeMake(3840, 2160); - resolutionTable[3] = CGSizeMake(safeAreaWidth, fullScreenHeight); - resolutionTable[4] = CGSizeMake(fullScreenWidth, fullScreenHeight); - resolutionTable[5] = CGSizeMake([currentSettings.width integerValue], [currentSettings.height integerValue]); // custom initial value + resolutionTable[2] = CGSizeMake(2560, 1440); + resolutionTable[3] = CGSizeMake(3840, 2160); + resolutionTable[4] = CGSizeMake(safeAreaWidth, fullScreenHeight); + resolutionTable[5] = CGSizeMake(fullScreenWidth, fullScreenHeight); + resolutionTable[6] = CGSizeMake([currentSettings.width integerValue], [currentSettings.height integerValue]); // custom initial value [self updateResolutionTable]; // Don't populate the custom entry unless we have a custom resolution @@ -1183,9 +1188,12 @@ BOOL isCustomResolution(CGSize res) { case 60: framerate = 1; break; - case 120: + case 90: framerate = 2; break; + case 120: + framerate = 3; + break; } NSInteger resolution = currentSettings.resolutionSelected.integerValue; @@ -1246,6 +1254,10 @@ BOOL isCustomResolution(CGSize res) { [self.yuv444Switch setOn:currentSettings.enableYUV444]; [self.statsOverlaySelector setSelectedSegmentIndex:currentSettings.statsOverlayLevel.intValue]; + + NSInteger renderingBackend = [currentSettings.renderingBackend integerValue]; + [self.renderingBackendSelector setSelectedSegmentIndex:renderingBackend]; + [self.citrixX1MouseSwitch setOn:currentSettings.btMouseSupport]; [self.optimizeGamesSwitch setOn: currentSettings.optimizeGames]; [self.framePacingSelector setSelectedSegmentIndex:currentSettings.useFramePacing ? 1 : 0]; @@ -1274,6 +1286,21 @@ BOOL isCustomResolution(CGSize res) { [self.bitrateSlider addTarget:self action:@selector(bitrateSliderMoved) forControlEvents:UIControlEventValueChanged]; [self updateBitrateText]; [self updateResolutionDisplayLabel]; + + [self.frameQueueSizeSlider setMinimumValue:1]; + [self.frameQueueSizeSlider setMaximumValue:5]; + [self.frameQueueSizeSlider setValue:_frameQueueSize]; + [self.frameQueueSizeSlider addTarget:self action:@selector(frameQueueSizeSliderMoved) forControlEvents:UIControlEventValueChanged]; + [self updateFrameQueueSizeText]; + + [self.enableGraphsSwitch addTarget:self action:@selector(enableGraphsChanged) forControlEvents:UIControlEventValueChanged]; + [self enableGraphsChanged]; + [self.graphOpacityStepper setMinimumValue:0]; + [self.graphOpacityStepper setMaximumValue:100]; + [self.graphOpacityStepper setValue:_graphOpacity]; + [self.graphOpacityStepper addTarget:self action:@selector(graphOpacityStepperMoved) forControlEvents:UIControlEventValueChanged]; + [self updateGraphOpacityText]; + if (@available(iOS 18.0, tvOS 18.0, *)) {}else{ [self.audioConfigSelector removeSegmentAtIndex:1 animated:false]; [self.audioConfigSelector removeSegmentAtIndex:1 animated:false]; // segment 2 goes away when you remove index 2 @@ -1808,6 +1835,8 @@ BOOL isCustomResolution(CGSize res) { case 1: return 60; case 2: + return 90; + case 3: return 120; default: abort(); @@ -1926,25 +1955,50 @@ BOOL isCustomResolution(CGSize res) { } } +- (void) frameQueueSizeSliderMoved { + assert(self.frameQueueSizeSlider.value >= 0 && self.frameQueueSizeSlider.value <= 5); + _frameQueueSize = (int)self.frameQueueSizeSlider.value; + [self updateFrameQueueSizeText]; +} + +- (void) updateFrameQueueSizeText { + [self.frameQueueSizeLabel setText:[NSString stringWithFormat:@"Frames to buffer: %ld", _frameQueueSize ]]; +} + +- (void) enableGraphsChanged { + [self.graphOpacityStepper setEnabled:[self.enableGraphsSwitch isOn]]; +} + +- (void) graphOpacityStepperMoved { + assert(self.graphOpacityStepper.value >= 0 && self.graphOpacityStepper.value <= 100); + _graphOpacity = (int)self.graphOpacityStepper.value; + [self updateGraphOpacityText]; +} + +- (void) updateGraphOpacityText { + [self.enableGraphsLabel setText:[NSString stringWithFormat:@"Performance Graphs - Opacity: %ld%%", _graphOpacity ]]; +} + - (void) saveSettings { DataManager* dataMan = [[DataManager alloc] init]; NSInteger framerate = [self getChosenFrameRate]; NSInteger height = [self getChosenStreamHeight]; NSInteger width = [self getChosenStreamWidth]; NSInteger audioConfig = [@[@2, @6, @8][[self.audioConfigSelector selectedSegmentIndex]] integerValue]; + NSInteger renderingBackend = [self.renderingBackendSelector selectedSegmentIndex]; NSInteger onscreenControls = [self.onScreenWidgetSelector selectedSegmentIndex]; NSInteger keyboardToggleFingers = self.softKeyboardGestureSelector.selectedSegmentIndex == 3 ? 20 : self.softKeyboardGestureSelector.selectedSegmentIndex+3; NSInteger oscLayoutToolFingers = (uint16_t)self->oswLayoutFingers; - + CGFloat slideToSettingsDistance = self.slideToMenuDistanceSlider.value; uint32_t slideToSettingsScreenEdge = [self getScreenEdgeFromSelector]; CGFloat pointerVelocityModeDivider = (CGFloat)(uint8_t)self.pointerVelocityModeDividerSlider.value/100; CGFloat touchPointerVelocityFactor = (CGFloat)(uint16_t)[self map_velocFactorDisplay_fromSliderValue:self.touchPointerVelocityFactorSlider.value]/100; CGFloat mousePointerVelocityFactor = (CGFloat)(uint16_t)[self map_velocFactorDisplay_fromSliderValue:self.mousePointerVelocityFactorSlider.value]/100; CGFloat gyroSensitivity = (CGFloat)(uint16_t)self.gyroSensitivitySlider.value/100; - + uint16_t touchMoveEventInterval = (uint16_t)self.touchMoveEventIntervalSlider.value; - + BOOL reverseMouseWheelDirection = [self.reverseMouseWheelDirectionSelector selectedSegmentIndex] == 1; NSInteger asyncNativeTouchPriority = 1; //BOOL liftStreamViewForKeyboard = [self.liftStreamViewForKeyboardSelector selectedSegmentIndex] == 1; @@ -1964,6 +2018,7 @@ BOOL isCustomResolution(CGSize res) { BOOL statsOverlayEnabled = statsOverlayLevel != 0; BOOL enableHdr = self.hdrSwitch.isOn; BOOL unlockDisplayOrientation = [self.unlockDisplayOrientationSelector selectedSegmentIndex] == 1; + BOOL enableGraphs = self.enableGraphsSwitch.isOn; NSInteger resolutionSelected = [self.resolutionSelector selectedSegmentIndex]; NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex]; NSInteger localMousePointerMode = [self.localMousePointerModeSelector selectedSegmentIndex]; @@ -2002,7 +2057,11 @@ BOOL isCustomResolution(CGSize res) { unlockDisplayOrientation:unlockDisplayOrientation resolutionSelected:resolutionSelected externalDisplayMode:externalDisplayMode - localMousePointerMode:localMousePointerMode]; + localMousePointerMode:localMousePointerMode + frameQueueSize:_frameQueueSize + enableGraphs:enableGraphs + graphOpacity:_graphOpacity + renderingBackend:renderingBackend]; } - (void)didReceiveMemoryWarning { diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 85188a3c..e907875e 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -50,7 +50,6 @@ UITapGestureRecognizer *_menuDoubleTapGestureRecognizer; UITapGestureRecognizer *_playPauseTapGestureRecognizer; UITextView *_overlayView; - uint16_t overlayLevel; UILabel *_stageLabel; UILabel *_tipLabel; UIActivityIndicatorView *_spinner; @@ -189,7 +188,6 @@ } _settings = [[[DataManager alloc] init] getSettings]; //StreamFrameViewController retrieve the settings here. - overlayLevel = _settings.statsOverlayLevel.intValue; [self configOscLayoutTool]; [self configSwipeGestures]; [self configZoomGestureAndAddStreamView]; @@ -583,7 +581,7 @@ } else [self.view addSubview:_overlayView]; // don't know why but this is necessary for reactivating overlay. - NSString* overlayText = [self->_streamMan getStatsOverlayText:overlayLevel]; + NSString* overlayText = [self->_streamMan getStatsOverlayText]; dispatch_async(dispatch_get_main_queue(), ^{ [self updateOverlayText:overlayText]; }); diff --git a/moonlight-common/moonlight-common-c b/moonlight-common/moonlight-common-c index 58902e34..6fbc8ff7 160000 --- a/moonlight-common/moonlight-common-c +++ b/moonlight-common/moonlight-common-c @@ -1 +1 @@ -Subproject commit 58902e342f6d53d6783c99fe79a03168d46cd56f +Subproject commit 6fbc8ff7b00ead903fff08e387847dd59837a339 diff --git a/third_party/implot b/third_party/implot deleted file mode 160000 index 3da8bd34..00000000 --- a/third_party/implot +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3da8bd34299965d3b0ab124df743fe3e076fa222