spice: support connecting from socket

This commit is contained in:
osy 2024-02-04 20:44:58 -08:00
parent d2d1418c43
commit 0ace64f22d
4 changed files with 90 additions and 10 deletions

View File

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

View File

@ -16,7 +16,11 @@
#import <Foundation/Foundation.h>
#import "UTMSpiceIODelegate.h"
#if defined(WITH_REMOTE)
#import "UTMRemoteConnectInterface.h"
#else
@import QEMUKitInternal;
#endif
#if !defined(WITH_USB)
@import CocoaSpiceNoUsb;
#else
@ -34,7 +38,11 @@ typedef NS_OPTIONS(NSUInteger, UTMSpiceIOOptions) {
NS_ASSUME_NONNULL_BEGIN
#if defined(WITH_REMOTE)
@interface UTMSpiceIO : NSObject<CSConnectionDelegate, UTMRemoteConnectInterface>
#else
@interface UTMSpiceIO : NSObject<CSConnectionDelegate, QEMUInterface>
#endif
@property (nonatomic, readonly, nullable) CSDisplay *primaryDisplay;
@property (nonatomic, readonly, nullable) CSInput *primaryInput;
@ -50,6 +58,7 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)init NS_UNAVAILABLE;
- (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;
- (BOOL)startWithError:(NSError * _Nullable *)error;

View File

@ -22,7 +22,9 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
@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, readwrite, nullable) CSDisplay *primaryDisplay;
@property (nonatomic) NSMutableArray<CSDisplay *> *mutableDisplays;
@ -35,7 +37,6 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
@property (nonatomic, nullable) CSConnection *spiceConnection;
@property (nonatomic, nullable) CSMain *spice;
@property (nonatomic, nullable, copy) NSURL *sharedDirectory;
@property (nonatomic) NSInteger port;
@property (nonatomic) BOOL dynamicResolutionSupported;
@property (nonatomic, readwrite) BOOL isConnected;
@ -72,10 +73,26 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
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 {
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.audioEnabled = (self.options & UTMSpiceIOOptionsHasAudio) == UTMSpiceIOOptionsHasAudio;
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
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 (error) {
@ -158,6 +177,9 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
self.primaryUsbManager = connection.usbManager;
[self.delegate spiceDidChangeUsbManager:connection.usbManager];
#endif
#if defined(WITH_REMOTE)
[self.connectDelegate remoteInterfaceDidConnect:self];
#endif
}
- (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 {
NSAssert(connection == self.spiceConnection, @"Unknown connection");
self.isConnected = NO;
#if defined(WITH_REMOTE)
[self.connectDelegate remoteInterface:self didErrorWithMessage:message];
#else
[self.connectDelegate qemuInterface:self didErrorWithMessage:message];
#endif
}
- (void)spiceDisplayCreated:(CSConnection *)connection display:(CSDisplay *)display {
@ -216,11 +242,15 @@ NSString *const kUTMErrorDomain = @"com.utmapp.utm";
- (void)spiceForwardedPortOpened:(CSConnection *)connection port:(CSPort *)port {
if ([port.name isEqualToString:@"org.qemu.monitor.qmp.0"]) {
UTMQemuPort *qemuPort = [[UTMQemuPort alloc] initFrom:port];
#if !defined(WITH_REMOTE)
[self.connectDelegate qemuInterface:self didCreateMonitorPort:qemuPort];
#endif
}
if ([port.name isEqualToString:@"org.qemu.guest_agent.0"]) {
UTMQemuPort *qemuPort = [[UTMQemuPort alloc] initFrom:port];
#if !defined(WITH_REMOTE)
[self.connectDelegate qemuInterface:self didCreateGuestAgentPort:qemuPort];
#endif
}
if ([port.name isEqualToString:@"com.utmapp.terminal.0"]) {
self.primarySerial = port;

View File

@ -1996,6 +1996,7 @@
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>"; };
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>"; };
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>"; };
@ -2868,6 +2869,7 @@
CE9B153E2B11A63E003A32DD /* UTMRemoteServer.swift */,
CE9B15452B12A87E003A32DD /* GenerateKey.h */,
CE9B15462B12A87E003A32DD /* GenerateKey.c */,
CECF02562B706ADD00409FC0 /* UTMRemoteConnectInterface.h */,
);
path = Remote;
sourceTree = "<group>";