spice: support connecting from socket
This commit is contained in:
parent
d2d1418c43
commit
0ace64f22d
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>";
|
||||
|
|
Loading…
Reference in New Issue