Преглед на файлове

spice: support connecting from socket

osy преди 1 година
родител
ревизия
0ace64f22d
променени са 4 файла, в които са добавени 90 реда и са изтрити 10 реда
  1. 39 0
      Remote/UTMRemoteConnectInterface.h
  2. 9 0
      Services/UTMSpiceIO.h
  3. 40 10
      Services/UTMSpiceIO.m
  4. 2 0
      UTM.xcodeproj/project.pbxproj

+ 39 - 0
Remote/UTMRemoteConnectInterface.h

@@ -0,0 +1,39 @@
+//
+// Copyright © 2024 osy. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol UTMRemoteConnectDelegate;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@protocol UTMRemoteConnectInterface <NSObject>
+
+@property (nonatomic, weak) id<UTMRemoteConnectDelegate> connectDelegate;
+
+- (BOOL)connectWithError:(NSError * _Nullable *)error;
+- (void)disconnect;
+
+@end
+
+@protocol UTMRemoteConnectDelegate <NSObject>
+
+- (void)remoteInterface:(id<UTMRemoteConnectInterface>)remoteInterface didErrorWithMessage:(NSString *)message;
+- (void)remoteInterfaceDidConnect:(id<UTMRemoteConnectInterface>)remoteInterface;
+
+@end
+
+NS_ASSUME_NONNULL_END

+ 9 - 0
Services/UTMSpiceIO.h

@@ -16,7 +16,11 @@
 
 
 #import <Foundation/Foundation.h>
 #import <Foundation/Foundation.h>
 #import "UTMSpiceIODelegate.h"
 #import "UTMSpiceIODelegate.h"
+#if defined(WITH_REMOTE)
+#import "UTMRemoteConnectInterface.h"
+#else
 @import QEMUKitInternal;
 @import QEMUKitInternal;
+#endif
 #if !defined(WITH_USB)
 #if !defined(WITH_USB)
 @import CocoaSpiceNoUsb;
 @import CocoaSpiceNoUsb;
 #else
 #else
@@ -34,7 +38,11 @@ typedef NS_OPTIONS(NSUInteger, UTMSpiceIOOptions) {
 
 
 NS_ASSUME_NONNULL_BEGIN
 NS_ASSUME_NONNULL_BEGIN
 
 
+#if defined(WITH_REMOTE)
+@interface UTMSpiceIO : NSObject<CSConnectionDelegate, UTMRemoteConnectInterface>
+#else
 @interface UTMSpiceIO : NSObject<CSConnectionDelegate, QEMUInterface>
 @interface UTMSpiceIO : NSObject<CSConnectionDelegate, QEMUInterface>
+#endif
 
 
 @property (nonatomic, readonly, nullable) CSDisplay *primaryDisplay;
 @property (nonatomic, readonly, nullable) CSDisplay *primaryDisplay;
 @property (nonatomic, readonly, nullable) CSInput *primaryInput;
 @property (nonatomic, readonly, nullable) CSInput *primaryInput;
@@ -50,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
 
 
 - (instancetype)init NS_UNAVAILABLE;
 - (instancetype)init NS_UNAVAILABLE;
 - (instancetype)initWithSocketUrl:(NSURL *)socketUrl options:(UTMSpiceIOOptions)options NS_DESIGNATED_INITIALIZER;
 - (instancetype)initWithSocketUrl:(NSURL *)socketUrl options:(UTMSpiceIOOptions)options NS_DESIGNATED_INITIALIZER;
+- (instancetype)initWithHost:(NSString *)host port:(NSInteger)port options:(UTMSpiceIOOptions)options NS_DESIGNATED_INITIALIZER;
 - (void)changeSharedDirectory:(NSURL *)url;
 - (void)changeSharedDirectory:(NSURL *)url;
 
 
 - (BOOL)startWithError:(NSError * _Nullable *)error;
 - (BOOL)startWithError:(NSError * _Nullable *)error;

+ 40 - 10
Services/UTMSpiceIO.m

@@ -22,7 +22,9 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
 
 
 @interface UTMSpiceIO ()
 @interface UTMSpiceIO ()
 
 
-@property (nonatomic) NSURL *socketUrl;
+@property (nonatomic, nullable) NSURL *socketUrl;
+@property (nonatomic, nullable) NSString *host;
+@property (nonatomic) NSInteger port;
 @property (nonatomic) UTMSpiceIOOptions options;
 @property (nonatomic) UTMSpiceIOOptions options;
 @property (nonatomic, readwrite, nullable) CSDisplay *primaryDisplay;
 @property (nonatomic, readwrite, nullable) CSDisplay *primaryDisplay;
 @property (nonatomic) NSMutableArray<CSDisplay *> *mutableDisplays;
 @property (nonatomic) NSMutableArray<CSDisplay *> *mutableDisplays;
@@ -35,7 +37,6 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
 @property (nonatomic, nullable) CSConnection *spiceConnection;
 @property (nonatomic, nullable) CSConnection *spiceConnection;
 @property (nonatomic, nullable) CSMain *spice;
 @property (nonatomic, nullable) CSMain *spice;
 @property (nonatomic, nullable, copy) NSURL *sharedDirectory;
 @property (nonatomic, nullable, copy) NSURL *sharedDirectory;
-@property (nonatomic) NSInteger port;
 @property (nonatomic) BOOL dynamicResolutionSupported;
 @property (nonatomic) BOOL dynamicResolutionSupported;
 @property (nonatomic, readwrite) BOOL isConnected;
 @property (nonatomic, readwrite) BOOL isConnected;
 
 
@@ -72,10 +73,26 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
     return self;
     return self;
 }
 }
 
 
+- (instancetype)initWithHost:(NSString *)host port:(NSInteger)port options:(UTMSpiceIOOptions)options {
+    if (self = [super init]) {
+        self.host = host;
+        self.port = port;
+        self.options = options;
+        self.mutableDisplays = [NSMutableArray array];
+        self.mutableSerials = [NSMutableArray array];
+    }
+
+    return self;
+}
+
 - (void)initializeSpiceIfNeeded {
 - (void)initializeSpiceIfNeeded {
     if (!self.spiceConnection) {
     if (!self.spiceConnection) {
-        NSURL *relativeSocketFile = [NSURL fileURLWithPath:self.socketUrl.lastPathComponent];
-        self.spiceConnection = [[CSConnection alloc] initWithUnixSocketFile:relativeSocketFile];
+        if (self.socketUrl) {
+            NSURL *relativeSocketFile = [NSURL fileURLWithPath:self.socketUrl.lastPathComponent];
+            self.spiceConnection = [[CSConnection alloc] initWithUnixSocketFile:relativeSocketFile];
+        } else {
+            self.spiceConnection = [[CSConnection alloc] initWithHost:self.host port:[@(self.port) stringValue]];
+        }
         self.spiceConnection.delegate = self;
         self.spiceConnection.delegate = self;
         self.spiceConnection.audioEnabled = (self.options & UTMSpiceIOOptionsHasAudio) == UTMSpiceIOOptionsHasAudio;
         self.spiceConnection.audioEnabled = (self.options & UTMSpiceIOOptionsHasAudio) == UTMSpiceIOOptionsHasAudio;
         self.spiceConnection.session.shareClipboard = (self.options & UTMSpiceIOOptionsHasClipboardSharing) == UTMSpiceIOOptionsHasClipboardSharing;
         self.spiceConnection.session.shareClipboard = (self.options & UTMSpiceIOOptionsHasClipboardSharing) == UTMSpiceIOOptionsHasClipboardSharing;
@@ -94,13 +111,15 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
     }
     }
     // do not need to encode/decode audio locally
     // do not need to encode/decode audio locally
     g_setenv("SPICE_DISABLE_OPUS", "1", YES);
     g_setenv("SPICE_DISABLE_OPUS", "1", YES);
-    // need to chdir to workaround AF_UNIX sun_len limitations
-    NSString *curdir = self.socketUrl.URLByDeletingLastPathComponent.path;
-    if (!curdir || ![NSFileManager.defaultManager changeCurrentDirectoryPath:curdir]) {
-        if (error) {
-            *error = [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Failed to change current directory.", "UTMSpiceIO")}];
+    if (self.socketUrl) {
+        // need to chdir to workaround AF_UNIX sun_len limitations
+        NSString *curdir = self.socketUrl.URLByDeletingLastPathComponent.path;
+        if (!curdir || ![NSFileManager.defaultManager changeCurrentDirectoryPath:curdir]) {
+            if (error) {
+                *error = [NSError errorWithDomain:kUTMErrorDomain code:-1 userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"Failed to change current directory.", "UTMSpiceIO")}];
+            }
+            return NO;
         }
         }
-        return NO;
     }
     }
     if (![self.spice spiceStart]) {
     if (![self.spice spiceStart]) {
         if (error) {
         if (error) {
@@ -158,6 +177,9 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
     self.primaryUsbManager = connection.usbManager;
     self.primaryUsbManager = connection.usbManager;
     [self.delegate spiceDidChangeUsbManager:connection.usbManager];
     [self.delegate spiceDidChangeUsbManager:connection.usbManager];
 #endif
 #endif
+#if defined(WITH_REMOTE)
+    [self.connectDelegate remoteInterfaceDidConnect:self];
+#endif
 }
 }
 
 
 - (void)spiceInputAvailable:(CSConnection *)connection input:(CSInput *)input {
 - (void)spiceInputAvailable:(CSConnection *)connection input:(CSInput *)input {
@@ -182,7 +204,11 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
 - (void)spiceError:(CSConnection *)connection code:(CSConnectionError)code message:(nullable NSString *)message {
 - (void)spiceError:(CSConnection *)connection code:(CSConnectionError)code message:(nullable NSString *)message {
     NSAssert(connection == self.spiceConnection, @"Unknown connection");
     NSAssert(connection == self.spiceConnection, @"Unknown connection");
     self.isConnected = NO;
     self.isConnected = NO;
+#if defined(WITH_REMOTE)
+    [self.connectDelegate remoteInterface:self didErrorWithMessage:message];
+#else
     [self.connectDelegate qemuInterface:self didErrorWithMessage:message];
     [self.connectDelegate qemuInterface:self didErrorWithMessage:message];
+#endif
 }
 }
 
 
 - (void)spiceDisplayCreated:(CSConnection *)connection display:(CSDisplay *)display {
 - (void)spiceDisplayCreated:(CSConnection *)connection display:(CSDisplay *)display {
@@ -216,11 +242,15 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
 - (void)spiceForwardedPortOpened:(CSConnection *)connection port:(CSPort *)port {
 - (void)spiceForwardedPortOpened:(CSConnection *)connection port:(CSPort *)port {
     if ([port.name isEqualToString:@"org.qemu.monitor.qmp.0"]) {
     if ([port.name isEqualToString:@"org.qemu.monitor.qmp.0"]) {
         UTMQemuPort *qemuPort = [[UTMQemuPort alloc] initFrom:port];
         UTMQemuPort *qemuPort = [[UTMQemuPort alloc] initFrom:port];
+#if !defined(WITH_REMOTE)
         [self.connectDelegate qemuInterface:self didCreateMonitorPort:qemuPort];
         [self.connectDelegate qemuInterface:self didCreateMonitorPort:qemuPort];
+#endif
     }
     }
     if ([port.name isEqualToString:@"org.qemu.guest_agent.0"]) {
     if ([port.name isEqualToString:@"org.qemu.guest_agent.0"]) {
         UTMQemuPort *qemuPort = [[UTMQemuPort alloc] initFrom:port];
         UTMQemuPort *qemuPort = [[UTMQemuPort alloc] initFrom:port];
+#if !defined(WITH_REMOTE)
         [self.connectDelegate qemuInterface:self didCreateGuestAgentPort:qemuPort];
         [self.connectDelegate qemuInterface:self didCreateGuestAgentPort:qemuPort];
+#endif
     }
     }
     if ([port.name isEqualToString:@"com.utmapp.terminal.0"]) {
     if ([port.name isEqualToString:@"com.utmapp.terminal.0"]) {
         self.primarySerial = port;
         self.primarySerial = port;

+ 2 - 0
UTM.xcodeproj/project.pbxproj

@@ -1996,6 +1996,7 @@
 		CEC794B9294924E300121A9F /* UTMScriptingSerialPortImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingSerialPortImpl.swift; sourceTree = "<group>"; };
 		CEC794B9294924E300121A9F /* UTMScriptingSerialPortImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMScriptingSerialPortImpl.swift; sourceTree = "<group>"; };
 		CEC794BB2949663C00121A9F /* UTMScripting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UTMScripting.swift; sourceTree = "<group>"; };
 		CEC794BB2949663C00121A9F /* UTMScripting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UTMScripting.swift; sourceTree = "<group>"; };
 		CEC9968328AA516000E7A025 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
 		CEC9968328AA516000E7A025 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
+		CECF02562B706ADD00409FC0 /* UTMRemoteConnectInterface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UTMRemoteConnectInterface.h; sourceTree = "<group>"; };
 		CED234EC254796E500ED0A57 /* NumberTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberTextField.swift; sourceTree = "<group>"; };
 		CED234EC254796E500ED0A57 /* NumberTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberTextField.swift; sourceTree = "<group>"; };
 		CED814E824C79F070042F0F1 /* VMConfigDriveCreateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfigDriveCreateView.swift; sourceTree = "<group>"; };
 		CED814E824C79F070042F0F1 /* VMConfigDriveCreateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfigDriveCreateView.swift; sourceTree = "<group>"; };
 		CED814EB24C7C2850042F0F1 /* VMConfigInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfigInfoView.swift; sourceTree = "<group>"; };
 		CED814EB24C7C2850042F0F1 /* VMConfigInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMConfigInfoView.swift; sourceTree = "<group>"; };
@@ -2868,6 +2869,7 @@
 				CE9B153E2B11A63E003A32DD /* UTMRemoteServer.swift */,
 				CE9B153E2B11A63E003A32DD /* UTMRemoteServer.swift */,
 				CE9B15452B12A87E003A32DD /* GenerateKey.h */,
 				CE9B15452B12A87E003A32DD /* GenerateKey.h */,
 				CE9B15462B12A87E003A32DD /* GenerateKey.c */,
 				CE9B15462B12A87E003A32DD /* GenerateKey.c */,
+				CECF02562B706ADD00409FC0 /* UTMRemoteConnectInterface.h */,
 			);
 			);
 			path = Remote;
 			path = Remote;
 			sourceTree = "<group>";
 			sourceTree = "<group>";