From b4469a387db8e020035fc6d34e2f97a051dd6993 Mon Sep 17 00:00:00 2001 From: Christoph Parstorfer Date: Wed, 14 Oct 2020 17:30:59 +0200 Subject: [PATCH] Add external extended display implementation --- CocoaSpice/CSDisplayMetal.h | 1 + CocoaSpice/CSDisplayMetal.m | 9 ++++-- Managers/UTMSpiceIO.h | 1 + Managers/UTMSpiceIO.m | 7 ++++ .../Display/VMDisplayMetalViewController.m | 2 +- ...VMExternalDisplayMetalViewController.swift | 31 +++++++++++++++--- Platform/iOS/ExternalScreen.swift | 32 +++++++------------ 7 files changed, 54 insertions(+), 29 deletions(-) diff --git a/CocoaSpice/CSDisplayMetal.h b/CocoaSpice/CSDisplayMetal.h index 0629600b2..18c0efdc4 100644 --- a/CocoaSpice/CSDisplayMetal.h +++ b/CocoaSpice/CSDisplayMetal.h @@ -37,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN - (id)initWithSession:(nonnull SpiceSession *)session channelID:(NSInteger)channelID; - (void)updateVisibleAreaWithRect:(CGRect)rect; - (void)requestResolution:(CGRect)bounds; +- (void)requestResolution:(CGRect)bounds monitorID:(NSInteger)monitorID; @end diff --git a/CocoaSpice/CSDisplayMetal.m b/CocoaSpice/CSDisplayMetal.m index 6091e8cbe..07b1ca20f 100644 --- a/CocoaSpice/CSDisplayMetal.m +++ b/CocoaSpice/CSDisplayMetal.m @@ -408,13 +408,18 @@ - (BOOL)visible { } - (void)requestResolution:(CGRect)bounds { + [self requestResolution:bounds monitorID:self.monitorID]; +} + +- (void)requestResolution:(CGRect)bounds monitorID:(NSInteger)monitorID { + if (!_main) { UTMLog(@"ignoring change resolution because main channel not found"); return; } - spice_main_channel_update_display_enabled(_main, (int)self.monitorID, TRUE, FALSE); + spice_main_channel_update_display_enabled(_main, (int)monitorID, TRUE, FALSE); spice_main_channel_update_display(_main, - (int)self.monitorID, + (int)monitorID, bounds.origin.x, bounds.origin.y, bounds.size.width, diff --git a/Managers/UTMSpiceIO.h b/Managers/UTMSpiceIO.h index 17ccb4231..78574f6bf 100644 --- a/Managers/UTMSpiceIO.h +++ b/Managers/UTMSpiceIO.h @@ -35,6 +35,7 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init NS_UNAVAILABLE; - (instancetype)initWithConfiguration: (UTMConfiguration*) configuration port:(NSInteger)port NS_DESIGNATED_INITIALIZER; - (void)changeSharedDirectory:(NSURL *)url; +- (CSConnection*)getSpiceConnection; @end diff --git a/Managers/UTMSpiceIO.m b/Managers/UTMSpiceIO.m index 2814f7e51..e0f77dd4e 100644 --- a/Managers/UTMSpiceIO.m +++ b/Managers/UTMSpiceIO.m @@ -162,6 +162,10 @@ - (void)restoreViewState:(UTMViewState *)viewState { self.primaryDisplay.viewportScale = viewState.displayScale; } +- (CSConnection *)getSpiceConnection { + return _spiceConnection; +} + #pragma mark - CSConnectionDelegate - (void)spiceConnected:(CSConnection *)connection { @@ -193,6 +197,9 @@ - (void)spiceDisplayCreated:(CSConnection *)connection display:(CSDisplayMetal * self.connectionCallback(YES, nil); self.connectionCallback = nil; } + } else { + // TODO external + [[NSNotificationCenter defaultCenter] postNotificationName:@"externalDisplayAdded" object:nil userInfo:@{@"display":display, @"input":input}]; } } diff --git a/Platform/iOS/Display/VMDisplayMetalViewController.m b/Platform/iOS/Display/VMDisplayMetalViewController.m index 1ed3f3f2d..63358455e 100644 --- a/Platform/iOS/Display/VMDisplayMetalViewController.m +++ b/Platform/iOS/Display/VMDisplayMetalViewController.m @@ -91,7 +91,7 @@ - (void)viewDidLoad { [self initPencilInteraction]; } // External screen - _externalController = [[ExternalScreenController alloc] initWithVmViewController:self metalView: self.mtkView sourceScreen: self.vmDisplay sourceCursor: self.vmInput]; + _externalController = [[ExternalScreenController alloc] initWithVmViewController:self metalView: self.mtkView]; } - (void)viewDidAppear:(BOOL)animated { diff --git a/Platform/iOS/Display/VMExternalDisplayMetalViewController.swift b/Platform/iOS/Display/VMExternalDisplayMetalViewController.swift index e5be2692e..e87f6caf4 100644 --- a/Platform/iOS/Display/VMExternalDisplayMetalViewController.swift +++ b/Platform/iOS/Display/VMExternalDisplayMetalViewController.swift @@ -16,17 +16,21 @@ import UIKit -class VMExternalDisplayMetalViewController: VMDisplayViewController { +class VMExternalDisplayMetalViewController: UIViewController { private let mtkView = MTKView() - - var renderer: UTMRenderer! var screenSize: CGSize! + + override func loadView() { + super.loadView() + NotificationCenter.default.addObserver(self, selector: #selector(handleDisplayAdded), name: .init(rawValue: "externalDisplayAdded"), object: nil) + } + + private var renderer: UTMRenderer! var sourceScreen: UTMRenderSource? var sourceCursor: UTMRenderSource? override func viewDidLoad() { - // don't call VMDisplayViewController.viewDidLoad because that inits the accesory view - (self as UIViewController).viewDidLoad() + super.viewDidLoad() view.addSubview(mtkView) mtkView.bindFrameToSuperviewBounds() @@ -37,8 +41,25 @@ class VMExternalDisplayMetalViewController: VMDisplayViewController { UTM.logger.critical("Metal is not supported on this device") return; } + } + + @objc func handleDisplayAdded(_ notification: Notification) { + print("Yippie kay jay Schweinebacke") + guard let userInfo = notification.userInfo, + let display = userInfo["display"] as? CSDisplayMetal, + let input = userInfo["input"] as? CSInput else { return } + sourceScreen = display + sourceCursor = input + renderer = UTMRenderer(metalKitView: mtkView) + renderer.sourceScreen = sourceScreen + renderer.sourceCursor = sourceCursor mtkView.delegate = renderer + renderer.mtkView(mtkView, drawableSizeWillChange: screenSize) } + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + NotificationCenter.default.removeObserver(self) + } } diff --git a/Platform/iOS/ExternalScreen.swift b/Platform/iOS/ExternalScreen.swift index cbaac5386..c74f98b4e 100644 --- a/Platform/iOS/ExternalScreen.swift +++ b/Platform/iOS/ExternalScreen.swift @@ -24,15 +24,10 @@ import UIKit private var externalWindow: UIWindow? private weak var externalVC: UIViewController? - private var sourceScreen: UTMRenderSource? - private var sourceCursor: UTMRenderSource? - - @objc init(vmViewController: VMDisplayMetalViewController, metalView: MTKView, sourceScreen: UTMRenderSource?, sourceCursor: UTMRenderSource?) { + @objc init(vmViewController: VMDisplayMetalViewController, metalView: MTKView) { super.init() self.vmViewController = vmViewController self.metalView = metalView - self.sourceScreen = sourceScreen - self.sourceCursor = sourceCursor // Listen for external screen events NotificationCenter.default.addObserver(forName: UIScreen.didConnectNotification, object: nil, queue: nil) { [weak self] notification in @@ -56,7 +51,7 @@ import UIKit if let window = self.externalWindow, window.screen == oldScreen { if let extVC = self.externalVC as? VMExternalDisplayMetalViewController { // discard external metal view - extVC.renderer = nil // ? + extVC.dismiss(animated: false, completion: nil) } else if let extVC = self.externalVC { extVC.view.removeFromSuperview() metalView.removeFromSuperview() @@ -104,11 +99,9 @@ import UIKit let newWindow = UIWindow(frame: screenFrame) externalWindow = newWindow newWindow.screen = newScreen - - metalView.removeFromSuperview() - makeExternalOnlyVC(newWindow: newWindow) -// makeExternalDuplicateVC(newWindow: newWindow) + makeExtendedDisplayVC(newWindow: newWindow) +// makeExternalOnlyVC(newWindow: newWindow) newWindow.isHidden = false @@ -123,23 +116,20 @@ import UIKit } } - private func makeExternalDuplicateVC(newWindow: UIWindow) { + private func makeExtendedDisplayVC(newWindow: UIWindow) { let extVC = VMExternalDisplayMetalViewController() extVC.screenSize = newWindow.bounds.size - extVC.renderer = (metalView!.delegate as! UTMRenderer) - extVC.sourceScreen = sourceScreen - extVC.sourceCursor = sourceCursor - newWindow.rootViewController = extVC extVC.loadView() - DispatchQueue.main.async { - let renderer = (self.metalView!.delegate as! UTMRenderer) - renderer.mtkView(self.metalView!, drawableSizeWillChange: self.externalWindow!.bounds.size) - self.metalView!.drawableSize = newWindow.bounds.size - } + newWindow.rootViewController = extVC self.externalVC = extVC + /// request `CSDisplay` to create a virtual display with the external window bounds + guard let mainDisplay = vmViewController.vmDisplay else { return } + let newMonitorID = mainDisplay.monitorID + 1 // TODO more than 1 external? + mainDisplay.requestResolution(newWindow.bounds, monitorID: newMonitorID) } private func makeExternalOnlyVC(newWindow: UIWindow) { + metalView.removeFromSuperview() let externalVC = UIViewController() newWindow.rootViewController = externalVC externalVC.view.addSubview(metalView)