2
0
Эх сурвалжийг харах

CocoaSpice: updated backend

osy 3 жил өмнө
parent
commit
2224ffadba

+ 1 - 1
Managers/UTMSpiceIO.h

@@ -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)

+ 22 - 13
Managers/UTMSpiceIO.m

@@ -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 (display.isPrimaryDisplay) {
+    if (self.primaryDisplay == display) {
+        [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];
     }
     }

+ 9 - 7
Managers/UTMSpiceIODelegate.h

@@ -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)spiceDidCreateDisplay:(CSDisplayMetal *)display;
-- (void)spiceDidDestroyDisplay:(CSDisplayMetal *)display;
-- (void)spiceDidCreateSerial:(CSPort *)serial;
-- (void)spiceDidDestroySerial:(CSPort *)serial;
+- (void)spiceDidCreateInput:(CSInput *)input NS_SWIFT_NAME(spiceDidCreateInput(_:));
+- (void)spiceDidDestroyInput:(CSInput *)input NS_SWIFT_NAME(spiceDidDestroyInput(_:));
+- (void)spiceDidCreateDisplay:(CSDisplay *)display NS_SWIFT_NAME(spiceDidCreateDisplay(_:));
+- (void)spiceDidDestroyDisplay:(CSDisplay *)display NS_SWIFT_NAME(spiceDidDestroyDisplay(_:));
+- (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

+ 1 - 1
Managers/UTMVirtualMachineDelegate.h

@@ -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

+ 3 - 3
Platform/iOS/Display/VMCursor.m

@@ -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.height = MAX(1, _controller.vmDisplay.cursorSize.height);
+    bounds.size.width = MAX(1, _controller.vmDisplay.cursor.cursorSize.width);
+    bounds.size.height = MAX(1, _controller.vmDisplay.cursor.cursorSize.height);
     return bounds;
     return bounds;
 }
 }
 
 

+ 1 - 1
Platform/iOS/Display/VMDisplayMetalViewController+Gamepad.m

@@ -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"

+ 1 - 1
Platform/iOS/Display/VMDisplayMetalViewController+Pointer.m

@@ -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"

+ 3 - 3
Platform/iOS/Display/VMDisplayMetalViewController+Touch.m

@@ -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];

+ 1 - 1
Platform/iOS/Display/VMDisplayMetalViewController.h

@@ -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;
 
 

+ 24 - 7
Platform/iOS/Display/VMDisplayMetalViewController.m

@@ -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 {
-    self.vmInput = input;
+- (void)spiceDidCreateInput:(CSInput *)input {
+    if (self.vmInput == nil) {
+        self.vmInput = input;
+    }
+}
+
+- (void)spiceDidDestroyInput:(CSInput *)input {
+    if (self.vmInput == input) {
+        self.vmInput = nil;
+    }
 }
 }
 
 
-- (void)spiceDidCreateDisplay:(CSDisplayMetal *)display {
-    if (display.isPrimaryDisplay) {
+- (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 {
-    // TODO: implement something here
+- (void)spiceDidDestroyDisplay:(CSDisplay *)display {
+    if (self.vmDisplay == display) {
+        self.vmDisplay = nil;
+        _renderer.source = nil;
+    }
+}
+
+- (void)spiceDidChangeDisplay:(CSDisplay *)display {
+    if (display == self.vmDisplay) {
+        
+    }
 }
 }
 
 
 @end
 @end

+ 9 - 3
Platform/iOS/Display/VMDisplayViewController.m

@@ -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 {

+ 26 - 15
Platform/macOS/Display/VMDisplayMetalWindowController.swift

@@ -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) {
-        vmInput = input
+    override func spiceDidCreateInput(_ input: CSInput) {
+        if vmInput == nil {
+            vmInput = input
+        }
+    }
+    
+    override func spiceDidDestroyInput(_ input: CSInput) {
+        if vmInput == input {
+            vmInput = nil
+        }
     }
     }
     
     
-    override func spiceDidCreateDisplay(_ display: CSDisplayMetal) {
-        if display.isPrimaryDisplay {
+    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) {
-        //TODO: implement something here
+    override func spiceDidDestroyDisplay(_ display: CSDisplay) {
+        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) {

+ 15 - 5
Platform/macOS/Display/VMDisplayQemuDisplayController.swift

@@ -208,24 +208,34 @@ 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
-            usbManager.delegate = self
+            if let usbManager = usbManager {
+                usbManager.delegate = self
+            }
         }
         }
     }
     }
     
     

+ 2 - 2
UTM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -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
         }
         }
       },
       },
@@ -58,4 +58,4 @@
     ]
     ]
   },
   },
   "version": 1
   "version": 1
-}
+}