CocoaSpice: updated backend

This commit is contained in:
osy 2022-05-27 12:06:29 -07:00
parent 489b12920e
commit 2224ffadba
14 changed files with 121 additions and 66 deletions

View File

@ -32,7 +32,7 @@ NS_ASSUME_NONNULL_BEGIN
@interface UTMSpiceIO : NSObject<CSConnectionDelegate> @interface UTMSpiceIO : NSObject<CSConnectionDelegate>
@property (nonatomic, readonly, nonnull) UTMQemuConfiguration* configuration; @property (nonatomic, readonly, nonnull) UTMQemuConfiguration* configuration;
@property (nonatomic, readonly, nullable) CSDisplayMetal *primaryDisplay; @property (nonatomic, readonly, nullable) CSDisplay *primaryDisplay;
@property (nonatomic, readonly, nullable) CSInput *primaryInput; @property (nonatomic, readonly, nullable) CSInput *primaryInput;
@property (nonatomic, readonly, nullable) CSPort *primarySerial; @property (nonatomic, readonly, nullable) CSPort *primarySerial;
#if !defined(WITH_QEMU_TCI) #if !defined(WITH_QEMU_TCI)

View File

@ -29,7 +29,7 @@ extern NSString *const kUTMErrorDomain;
@interface UTMSpiceIO () @interface UTMSpiceIO ()
@property (nonatomic, readwrite, nullable) CSDisplayMetal *primaryDisplay; @property (nonatomic, readwrite, nullable) CSDisplay *primaryDisplay;
@property (nonatomic, readwrite, nullable) CSInput *primaryInput; @property (nonatomic, readwrite, nullable) CSInput *primaryInput;
@property (nonatomic, readwrite, nullable) CSPort *primarySerial; @property (nonatomic, readwrite, nullable) CSPort *primarySerial;
#if !defined(WITH_QEMU_TCI) #if !defined(WITH_QEMU_TCI)
@ -147,14 +147,26 @@ extern NSString *const kUTMErrorDomain;
- (void)spiceConnected:(CSConnection *)connection { - (void)spiceConnected:(CSConnection *)connection {
NSAssert(connection == self.spiceConnection, @"Unknown connection"); NSAssert(connection == self.spiceConnection, @"Unknown connection");
self.isConnected = YES; self.isConnected = YES;
self.primaryInput = connection.input;
[self.delegate spiceDidChangeInput:connection.input];
#if !defined(WITH_QEMU_TCI) #if !defined(WITH_QEMU_TCI)
self.primaryUsbManager = connection.usbManager; self.primaryUsbManager = connection.usbManager;
[self.delegate spiceDidChangeUsbManager:connection.usbManager]; [self.delegate spiceDidChangeUsbManager:connection.usbManager];
#endif #endif
} }
- (void)spiceInputAvailable:(CSConnection *)connection input:(CSInput *)input {
if (self.primaryInput == nil) {
self.primaryInput = input;
[self.delegate spiceDidCreateInput:input];
}
}
- (void)spiceInputUnavailable:(CSConnection *)connection input:(CSInput *)input {
if (self.primaryInput == input) {
self.primaryInput = nil;
[self.delegate spiceDidDestroyInput:input];
}
}
- (void)spiceDisconnected:(CSConnection *)connection { - (void)spiceDisconnected:(CSConnection *)connection {
NSAssert(connection == self.spiceConnection, @"Unknown connection"); NSAssert(connection == self.spiceConnection, @"Unknown connection");
self.isConnected = NO; self.isConnected = NO;
@ -171,15 +183,17 @@ extern NSString *const kUTMErrorDomain;
}); });
} }
- (void)spiceDisplayCreated:(CSConnection *)connection display:(CSDisplayMetal *)display { - (void)spiceDisplayCreatedOrUpdated:(CSConnection *)connection display:(CSDisplay *)display {
NSAssert(connection == self.spiceConnection, @"Unknown connection"); NSAssert(connection == self.spiceConnection, @"Unknown connection");
[self.delegate spiceDidCreateDisplay:display]; if (self.primaryDisplay == display) {
if (display.isPrimaryDisplay) { [self.delegate spiceDidChangeDisplay:display];
} else if (display.isPrimaryDisplay) {
self.primaryDisplay = display; self.primaryDisplay = display;
[self.delegate spiceDidCreateDisplay:display];
} }
} }
- (void)spiceDisplayDestroyed:(CSConnection *)connection display:(CSDisplayMetal *)display { - (void)spiceDisplayDestroyed:(CSConnection *)connection display:(CSDisplay *)display {
NSAssert(connection == self.spiceConnection, @"Unknown connection"); NSAssert(connection == self.spiceConnection, @"Unknown connection");
[self.delegate spiceDidDestroyDisplay:display]; [self.delegate spiceDidDestroyDisplay:display];
} }
@ -248,16 +262,11 @@ extern NSString *const kUTMErrorDomain;
_delegate = delegate; _delegate = delegate;
// make sure to send initial data // make sure to send initial data
if (self.primaryInput) { if (self.primaryInput) {
[self.delegate spiceDidChangeInput:self.primaryInput]; [self.delegate spiceDidCreateInput:self.primaryInput];
} }
if (self.primaryDisplay) { if (self.primaryDisplay) {
[self.delegate spiceDidCreateDisplay:self.primaryDisplay]; [self.delegate spiceDidCreateDisplay:self.primaryDisplay];
} }
for (CSDisplayMetal *display in self.spiceConnection.monitors) {
if (display != self.primaryDisplay) {
[self.delegate spiceDidCreateDisplay:display];
}
}
if (self.primarySerial) { if (self.primarySerial) {
[self.delegate spiceDidCreateSerial:self.primarySerial]; [self.delegate spiceDidCreateSerial:self.primarySerial];
} }

View File

@ -16,7 +16,7 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
@class CSDisplayMetal; @class CSDisplay;
@class CSInput; @class CSInput;
@class CSPort; @class CSPort;
@class CSUSBManager; @class CSUSBManager;
@ -25,13 +25,15 @@ NS_ASSUME_NONNULL_BEGIN
@protocol UTMSpiceIODelegate<NSObject> @protocol UTMSpiceIODelegate<NSObject>
- (void)spiceDidChangeInput:(CSInput *)input; - (void)spiceDidCreateInput:(CSInput *)input NS_SWIFT_NAME(spiceDidCreateInput(_:));
- (void)spiceDidCreateDisplay:(CSDisplayMetal *)display; - (void)spiceDidDestroyInput:(CSInput *)input NS_SWIFT_NAME(spiceDidDestroyInput(_:));
- (void)spiceDidDestroyDisplay:(CSDisplayMetal *)display; - (void)spiceDidCreateDisplay:(CSDisplay *)display NS_SWIFT_NAME(spiceDidCreateDisplay(_:));
- (void)spiceDidCreateSerial:(CSPort *)serial; - (void)spiceDidDestroyDisplay:(CSDisplay *)display NS_SWIFT_NAME(spiceDidDestroyDisplay(_:));
- (void)spiceDidDestroySerial:(CSPort *)serial; - (void)spiceDidChangeDisplay:(CSDisplay *)display NS_SWIFT_NAME(spiceDidChangeDisplay(_:));
- (void)spiceDidCreateSerial:(CSPort *)serial NS_SWIFT_NAME(spiceDidCreateSerial(_:));
- (void)spiceDidDestroySerial:(CSPort *)serial NS_SWIFT_NAME(spiceDidDestroySerial(_:));
#if !defined(WITH_QEMU_TCI) #if !defined(WITH_QEMU_TCI)
- (void)spiceDidChangeUsbManager:(CSUSBManager *)usbManager; - (void)spiceDidChangeUsbManager:(nullable CSUSBManager *)usbManager NS_SWIFT_NAME(spiceDidChangeUsbManager(_:));
#endif #endif
@optional @optional

View File

@ -16,7 +16,7 @@
@protocol UTMConfigurable; @protocol UTMConfigurable;
@class UTMVirtualMachine; @class UTMVirtualMachine;
@class CSDisplayMetal; @class CSDisplay;
@class CSInput; @class CSInput;
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN

View File

@ -16,7 +16,7 @@
#import "VMCursor.h" #import "VMCursor.h"
#import "VMDisplayMetalViewController+Touch.h" #import "VMDisplayMetalViewController+Touch.h"
#import "CSDisplayMetal.h" #import "CSDisplay.h"
@interface VMCursor () @interface VMCursor ()
@ -64,8 +64,8 @@
- (CGRect)bounds { - (CGRect)bounds {
CGRect bounds = CGRectZero; CGRect bounds = CGRectZero;
bounds.size.width = MAX(1, _controller.vmDisplay.cursorSize.width); bounds.size.width = MAX(1, _controller.vmDisplay.cursor.cursorSize.width);
bounds.size.height = MAX(1, _controller.vmDisplay.cursorSize.height); bounds.size.height = MAX(1, _controller.vmDisplay.cursor.cursorSize.height);
return bounds; return bounds;
} }

View File

@ -19,7 +19,7 @@
#import "VMScroll.h" #import "VMScroll.h"
#import "VMDisplayMetalViewController+Gamepad.h" #import "VMDisplayMetalViewController+Gamepad.h"
#import "VMDisplayMetalViewController+Touch.h" #import "VMDisplayMetalViewController+Touch.h"
#import "CSDisplayMetal.h" #import "CSDisplay.h"
#import "UTMQemuConfiguration.h" #import "UTMQemuConfiguration.h"
#import "UTMQemuConfiguration+Constants.h" #import "UTMQemuConfiguration+Constants.h"
#import "UTMLogging.h" #import "UTMLogging.h"

View File

@ -20,7 +20,7 @@
#import "VMDisplayMetalViewController+Touch.h" #import "VMDisplayMetalViewController+Touch.h"
#import "VMDisplayMetalViewController+Pointer.h" #import "VMDisplayMetalViewController+Pointer.h"
#import "VMCursor.h" #import "VMCursor.h"
#import "CSDisplayMetal.h" #import "CSDisplay.h"
#import "VMScroll.h" #import "VMScroll.h"
#import "UTMQemuVirtualMachine.h" #import "UTMQemuVirtualMachine.h"
#import "UTMQemuVirtualMachine+SPICE.h" #import "UTMQemuVirtualMachine+SPICE.h"

View File

@ -20,7 +20,7 @@
#import "VMDisplayMetalViewController+Pencil.h" #import "VMDisplayMetalViewController+Pencil.h"
#import "VMCursor.h" #import "VMCursor.h"
#import "VMScroll.h" #import "VMScroll.h"
#import "CSDisplayMetal.h" #import "CSDisplay.h"
#import "UTMQemuConfiguration.h" #import "UTMQemuConfiguration.h"
#import "UTMQemuConfiguration+Miscellaneous.h" #import "UTMQemuConfiguration+Miscellaneous.h"
#import "UTMSpiceIO.h" #import "UTMSpiceIO.h"
@ -353,7 +353,7 @@ static CGFloat CGPointToPixel(CGFloat point) {
translated = [self clipCursorToDisplay:translated]; translated = [self clipCursorToDisplay:translated];
if (!self.vmInput.serverModeCursor) { if (!self.vmInput.serverModeCursor) {
[self.vmInput sendMousePosition:self.mouseButtonDown absolutePoint:translated]; [self.vmInput sendMousePosition:self.mouseButtonDown absolutePoint:translated];
[self.vmDisplay forceCursorPosition:translated]; // required to show cursor on screen [self.vmDisplay.cursor moveTo:translated]; // required to show cursor on screen
} else { } else {
UTMLog(@"Warning: ignored mouse set (%f, %f) while mouse is in server mode", translated.x, translated.y); UTMLog(@"Warning: ignored mouse set (%f, %f) while mouse is in server mode", translated.x, translated.y);
} }
@ -625,7 +625,7 @@ static CGFloat CGPointToPixel(CGFloat point) {
- (BOOL)switchMouseType:(VMMouseType)type { - (BOOL)switchMouseType:(VMMouseType)type {
BOOL shouldHideCursor = (type == VMMouseTypeAbsoluteHideCursor); BOOL shouldHideCursor = (type == VMMouseTypeAbsoluteHideCursor);
BOOL shouldUseServerMouse = (type == VMMouseTypeRelative); BOOL shouldUseServerMouse = (type == VMMouseTypeRelative);
self.vmDisplay.inhibitCursor = shouldHideCursor; self.vmDisplay.cursor.isInhibited = shouldHideCursor;
if (shouldUseServerMouse != self.vmInput.serverModeCursor) { if (shouldUseServerMouse != self.vmInput.serverModeCursor) {
UTMLog(@"Switching mouse mode to server:%d for type:%ld", shouldUseServerMouse, type); UTMLog(@"Switching mouse mode to server:%d for type:%ld", shouldUseServerMouse, type);
[self.vm requestInputTablet:!shouldUseServerMouse]; [self.vm requestInputTablet:!shouldUseServerMouse];

View File

@ -64,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic) IBOutlet VMKeyboardView *keyboardView; @property (nonatomic) IBOutlet VMKeyboardView *keyboardView;
@property (weak, nonatomic) CSInput *vmInput; @property (weak, nonatomic) CSInput *vmInput;
@property (weak, nonatomic) CSDisplayMetal *vmDisplay; @property (weak, nonatomic) CSDisplay *vmDisplay;
@property (nonatomic, readonly) BOOL serverModeCursor; @property (nonatomic, readonly) BOOL serverModeCursor;

View File

@ -26,7 +26,7 @@
#import "UTMQemuConfiguration.h" #import "UTMQemuConfiguration.h"
#import "UTMQemuConfiguration+Display.h" #import "UTMQemuConfiguration+Display.h"
#import "UTMLogging.h" #import "UTMLogging.h"
#import "CSDisplayMetal.h" #import "CSDisplay.h"
#import "UTM-Swift.h" #import "UTM-Swift.h"
@import CocoaSpiceRenderer; @import CocoaSpiceRenderer;
@ -200,12 +200,20 @@
#pragma mark - SPICE IO Delegates #pragma mark - SPICE IO Delegates
- (void)spiceDidChangeInput:(CSInput *)input { - (void)spiceDidCreateInput:(CSInput *)input {
if (self.vmInput == nil) {
self.vmInput = input; self.vmInput = input;
}
} }
- (void)spiceDidCreateDisplay:(CSDisplayMetal *)display { - (void)spiceDidDestroyInput:(CSInput *)input {
if (display.isPrimaryDisplay) { if (self.vmInput == input) {
self.vmInput = nil;
}
}
- (void)spiceDidCreateDisplay:(CSDisplay *)display {
if (self.vmDisplay == nil && display.isPrimaryDisplay) {
self.vmDisplay = display; self.vmDisplay = display;
_renderer.source = display; _renderer.source = display;
// restore last size // restore last size
@ -224,8 +232,17 @@
} }
} }
- (void)spiceDidDestroyDisplay:(CSDisplayMetal *)display { - (void)spiceDidDestroyDisplay:(CSDisplay *)display {
// TODO: implement something here if (self.vmDisplay == display) {
self.vmDisplay = nil;
_renderer.source = nil;
}
}
- (void)spiceDidChangeDisplay:(CSDisplay *)display {
if (display == self.vmDisplay) {
}
} }
@end @end

View File

@ -99,13 +99,19 @@
#pragma mark - SPICE IO Delegates #pragma mark - SPICE IO Delegates
- (void)spiceDidChangeInput:(CSInput *)input { - (void)spiceDidCreateInput:(CSInput *)input {
} }
- (void)spiceDidCreateDisplay:(CSDisplayMetal *)display { - (void)spiceDidDestroyInput:(CSInput *)input {
} }
- (void)spiceDidDestroyDisplay:(CSDisplayMetal *)display { - (void)spiceDidCreateDisplay:(CSDisplay *)display {
}
- (void)spiceDidChangeDisplay:(CSDisplay *)display {
}
- (void)spiceDidDestroyDisplay:(CSDisplay *)display {
} }
- (void)spiceDidCreateSerial:(CSPort *)serial { - (void)spiceDidCreateSerial:(CSPort *)serial {

View File

@ -20,10 +20,9 @@ class VMDisplayMetalWindowController: VMDisplayQemuWindowController {
var metalView: VMMetalView! var metalView: VMMetalView!
var renderer: CSRenderer? var renderer: CSRenderer?
private weak var vmDisplay: CSDisplayMetal? private weak var vmDisplay: CSDisplay?
private weak var vmInput: CSInput? private weak var vmInput: CSInput?
private var displaySizeObserver: NSKeyValueObservation?
private var displaySize: CGSize = .zero private var displaySize: CGSize = .zero
private var isDisplaySizeDynamic: Bool = false private var isDisplaySizeDynamic: Bool = false
private var isFullScreen: Bool = false private var isFullScreen: Bool = false
@ -77,10 +76,6 @@ class VMDisplayMetalWindowController: VMDisplayQemuWindowController {
override func enterLive() { override func enterLive() {
metalView.isHidden = false metalView.isHidden = false
screenshotView.isHidden = true screenshotView.isHidden = true
displaySizeObserver = observe(\.vmDisplay!.displaySize, options: [.initial, .new]) { (_, change) in
guard let size = change.newValue else { return }
self.displaySizeDidChange(size: size)
}
if vmQemuConfig!.shareClipboardEnabled { if vmQemuConfig!.shareClipboardEnabled {
UTMPasteboard.general.requestPollingMode(forHashable: self) // start clipboard polling UTMPasteboard.general.requestPollingMode(forHashable: self) // start clipboard polling
} }
@ -121,7 +116,6 @@ class VMDisplayMetalWindowController: VMDisplayQemuWindowController {
self.globalEventMonitor = nil self.globalEventMonitor = nil
} }
releaseMouse() releaseMouse()
displaySizeObserver = nil
super.enterSuspended(isBusy: busy) super.enterSuspended(isBusy: busy)
} }
@ -132,19 +126,36 @@ class VMDisplayMetalWindowController: VMDisplayQemuWindowController {
// MARK: - SPICE IO // MARK: - SPICE IO
extension VMDisplayMetalWindowController { extension VMDisplayMetalWindowController {
override func spiceDidChange(_ input: CSInput) { override func spiceDidCreateInput(_ input: CSInput) {
if vmInput == nil {
vmInput = input vmInput = input
} }
}
override func spiceDidCreateDisplay(_ display: CSDisplayMetal) { override func spiceDidDestroyInput(_ input: CSInput) {
if display.isPrimaryDisplay { if vmInput == input {
vmInput = nil
}
}
override func spiceDidCreateDisplay(_ display: CSDisplay) {
if vmDisplay == nil && display.isPrimaryDisplay {
vmDisplay = display vmDisplay = display
renderer!.source = vmDisplay renderer!.source = display
} }
} }
override func spiceDidDestroyDisplay(_ display: CSDisplayMetal) { override func spiceDidDestroyDisplay(_ display: CSDisplay) {
//TODO: implement something here if vmDisplay == display {
vmDisplay = nil
renderer!.source = nil
}
}
override func spiceDidChangeDisplay(_ display: CSDisplay) {
if vmDisplay == display {
displaySizeDidChange(size: display.displaySize)
}
} }
override func spiceDynamicResolutionSupportDidChange(_ supported: Bool) { override func spiceDynamicResolutionSupportDidChange(_ supported: Bool) {
@ -345,7 +356,7 @@ extension VMDisplayMetalWindowController: VMMetalViewInputDelegate {
let point = CGPoint(x: newX, y: newY) let point = CGPoint(x: newX, y: newY)
logger.trace("move cursor: cocoa (\(absolutePoint.x), \(absolutePoint.y)), native (\(newX), \(newY))") logger.trace("move cursor: cocoa (\(absolutePoint.x), \(absolutePoint.y)), native (\(newX), \(newY))")
vmInput.sendMousePosition(button, absolutePoint: point) vmInput.sendMousePosition(button, absolutePoint: point)
vmDisplay?.forceCursorPosition(point) // required to show cursor on screen vmDisplay?.cursor?.move(to: point) // required to show cursor on screen
} }
func mouseMove(relativePoint: CGPoint, button: CSInputButton) { func mouseMove(relativePoint: CGPoint, button: CSInputButton) {

View File

@ -208,26 +208,36 @@ extension VMDisplayQemuWindowController {
// MARK: - SPICE base implementation // MARK: - SPICE base implementation
extension VMDisplayQemuWindowController: UTMSpiceIODelegate { extension VMDisplayQemuWindowController: UTMSpiceIODelegate {
func spiceDidChange(_ input: CSInput) { func spiceDidCreateInput(_ input: CSInput) {
// Implemented in subclass // Implemented in subclass
} }
func spiceDidCreateDisplay(_ display: CSDisplayMetal) { func spiceDidDestroyInput(_ input: CSInput) {
// Implemented in subclass // Implemented in subclass
} }
func spiceDidDestroyDisplay(_ display: CSDisplayMetal) { func spiceDidCreateDisplay(_ display: CSDisplay) {
// Implemented in subclass // Implemented in subclass
} }
func spiceDidChange(_ usbManager: CSUSBManager) { func spiceDidChangeDisplay(_ display: CSDisplay) {
// Implemented in subclass
}
func spiceDidDestroyDisplay(_ display: CSDisplay) {
// Implemented in subclass
}
func spiceDidChangeUsbManager(_ usbManager: CSUSBManager?) {
if usbManager != vmUsbManager { if usbManager != vmUsbManager {
connectedUsbDevices.removeAll() connectedUsbDevices.removeAll()
allUsbDevices.removeAll() allUsbDevices.removeAll()
vmUsbManager = usbManager vmUsbManager = usbManager
if let usbManager = usbManager {
usbManager.delegate = self usbManager.delegate = self
} }
} }
}
func spiceDynamicResolutionSupportDidChange(_ supported: Bool) { func spiceDynamicResolutionSupportDidChange(_ supported: Bool) {
// Implemented in subclass // Implemented in subclass

View File

@ -15,7 +15,7 @@
"repositoryURL": "https://github.com/utmapp/CocoaSpice.git", "repositoryURL": "https://github.com/utmapp/CocoaSpice.git",
"state": { "state": {
"branch": "main", "branch": "main",
"revision": "6a210bc6dd50d12bd57f990bd4d062d0fd93d529", "revision": "5070355c1ca5706c50bd74e8d65f89debd7bf8a0",
"version": null "version": null
} }
}, },