remote: hide QEMU internals from remote SPICE VM

This commit is contained in:
osy 2024-01-28 17:52:50 -08:00
parent 80b88bf868
commit 58df784f3d
7 changed files with 74 additions and 77 deletions

View File

@ -165,7 +165,7 @@ struct ContentView: View {
case "pause":
if let vm = findVM(), vm.state == .started {
let shouldSaveOnPause: Bool
if let vm = vm.wrapped as? (any UTMQemuSpiceVirtualMachine) {
if let vm = vm.wrapped as? (any UTMSpiceVirtualMachine) {
shouldSaveOnPause = !vm.isRunningAsDisposible
} else {
shouldSaveOnPause = true

View File

@ -26,8 +26,8 @@ struct VMRemovableDrivesView: View {
@State private var workaroundFileImporterBug: Bool = false
@State private var currentDrive: UTMQemuConfigurationDrive?
private var qemuVM: (any UTMQemuSpiceVirtualMachine)! {
vm.wrapped as? any UTMQemuSpiceVirtualMachine
private var qemuVM: (any UTMSpiceVirtualMachine)! {
vm.wrapped as? any UTMSpiceVirtualMachine
}
var fileManager: FileManager {

View File

@ -456,7 +456,7 @@ class VMRemoteData: VMData {
}
let entry = registryEntryWrapped!
let config = try await server.getQEMUConfiguration(for: entry.uuid)
let vm = UTMRemoteQemuVirtualMachine(forRemoteServer: server, remotePath: entry.package.path, entry: entry, config: config)
let vm = UTMRemoteSpiceVirtualMachine(forRemoteServer: server, remotePath: entry.package.path, entry: entry, config: config)
wrapped = vm
vm.updateConfigFromRegistry()
subscribeToChildren()

View File

@ -16,7 +16,7 @@
import Foundation
final class UTMRemoteQemuVirtualMachine: UTMQemuSpiceVirtualMachine {
final class UTMRemoteSpiceVirtualMachine: UTMSpiceVirtualMachine {
struct Capabilities: UTMVirtualMachineCapabilities {
var supportsProcessKill: Bool {
true
@ -91,10 +91,6 @@ final class UTMRemoteQemuVirtualMachine: UTMQemuSpiceVirtualMachine {
}
}
private(set) var monitor: QEMUMonitor?
private(set) var guestAgent: QEMUGuestAgent?
var changeCursorRequestInProgress: Bool = false
func reload(from packageUrl: URL?) throws {
@ -110,7 +106,7 @@ final class UTMRemoteQemuVirtualMachine: UTMQemuSpiceVirtualMachine {
}
}
extension UTMRemoteQemuVirtualMachine {
extension UTMRemoteSpiceVirtualMachine {
func start(options: UTMVirtualMachineStartOptions) async throws {
}
@ -132,7 +128,13 @@ extension UTMRemoteQemuVirtualMachine {
}
}
extension UTMRemoteQemuVirtualMachine {
extension UTMRemoteSpiceVirtualMachine {
func requestInputTablet(_ tablet: Bool) {
}
}
extension UTMRemoteSpiceVirtualMachine {
func eject(_ drive: UTMQemuConfigurationDrive) async throws {
}
@ -143,7 +145,7 @@ extension UTMRemoteQemuVirtualMachine {
}
extension UTMRemoteQemuVirtualMachine {
extension UTMRemoteSpiceVirtualMachine {
func stopAccessingPath(_ path: String) async {
}
@ -153,7 +155,7 @@ extension UTMRemoteQemuVirtualMachine {
}
}
extension UTMRemoteQemuVirtualMachine {
extension UTMRemoteSpiceVirtualMachine {
enum VMError: LocalizedError {
}
}

View File

@ -22,7 +22,7 @@ private let kSuspendSnapshotName = "suspend"
private let kProbeSuspendDelay = 1*NSEC_PER_SEC
/// QEMU backend virtual machine
final class UTMQemuVirtualMachine: UTMQemuSpiceVirtualMachine {
final class UTMQemuVirtualMachine: UTMSpiceVirtualMachine {
struct Capabilities: UTMVirtualMachineCapabilities {
var supportsProcessKill: Bool {
true
@ -143,8 +143,8 @@ final class UTMQemuVirtualMachine: UTMQemuSpiceVirtualMachine {
private var swtpm: UTMSWTPM?
var changeCursorRequestInProgress: Bool = false
private var changeCursorRequestInProgress: Bool = false
@MainActor required init(packageUrl: URL, configuration: UTMQemuConfiguration, isShortcut: Bool = false) throws {
self.isScopedAccess = packageUrl.startAccessingSecurityScopedResource()
// load configuration
@ -619,6 +619,37 @@ extension UTMQemuVirtualMachine: QEMUVirtualMachineDelegate {
}
}
// MARK: - Input device switching
extension UTMQemuVirtualMachine {
func requestInputTablet(_ tablet: Bool) {
guard !changeCursorRequestInProgress else {
return
}
guard let spiceIO = ioService else {
return
}
changeCursorRequestInProgress = true
Task {
defer {
changeCursorRequestInProgress = false
}
guard state == .started else {
return
}
guard let monitor = await monitor else {
return
}
do {
let index = try await monitor.mouseIndex(forAbsolute: tablet)
try await monitor.mouseSelect(index)
spiceIO.primaryInput?.requestMouseMode(!tablet)
} catch {
logger.error("Error changing mouse mode: \(error)")
}
}
}
}
// MARK: - Architecture supported
extension UTMQemuVirtualMachine {
/// Check if a QEMU target is supported

View File

@ -17,8 +17,8 @@
import Foundation
import QEMUKit
/// Common methods for all SPICE+QEMU virtual machines
protocol UTMQemuSpiceVirtualMachine: UTMVirtualMachine where Configuration == UTMQemuConfiguration {
/// Common methods for all SPICE virtual machines
protocol UTMSpiceVirtualMachine: UTMVirtualMachine where Configuration == UTMQemuConfiguration {
/// Set when VM is running with saving changes
var isRunningAsDisposible: Bool { get }
@ -31,15 +31,10 @@ protocol UTMQemuSpiceVirtualMachine: UTMVirtualMachine where Configuration == UT
/// SPICE interface
var ioService: UTMSpiceIO? { get }
/// QEMU monitor interface
var monitor: QEMUMonitor? { get async }
/// QEMU GA interface
var guestAgent: QEMUGuestAgent? { get async }
/// Set when cursor change is requested (to debounce the request)
var changeCursorRequestInProgress: Bool { get set }
/// Change input mode
/// - Parameter tablet: If true, mouse events will be absolute
func requestInputTablet(_ tablet: Bool)
/// Eject a removable drive
/// - Parameter drive: Removable drive
func eject(_ drive: UTMQemuConfigurationDrive) async throws
@ -63,46 +58,15 @@ protocol UTMQemuSpiceVirtualMachine: UTMVirtualMachine where Configuration == UT
func changeVirtfsSharedDirectory(with bookmark: Data, isSecurityScoped: Bool) async throws
}
// MARK: - Input device switching
extension UTMQemuSpiceVirtualMachine {
func requestInputTablet(_ tablet: Bool) {
guard !changeCursorRequestInProgress else {
return
}
guard let spiceIO = ioService else {
return
}
changeCursorRequestInProgress = true
Task {
defer {
changeCursorRequestInProgress = false
}
guard state == .started else {
return
}
guard let monitor = await monitor else {
return
}
do {
let index = try await monitor.mouseIndex(forAbsolute: tablet)
try await monitor.mouseSelect(index)
spiceIO.primaryInput?.requestMouseMode(!tablet)
} catch {
logger.error("Error changing mouse mode: \(error)")
}
}
}
}
// MARK: - USB redirection
extension UTMQemuSpiceVirtualMachine {
extension UTMSpiceVirtualMachine {
var hasUsbRedirection: Bool {
return jb_has_usb_entitlement()
}
}
// MARK: - Screenshot
extension UTMQemuSpiceVirtualMachine {
extension UTMSpiceVirtualMachine {
@MainActor @discardableResult
func takeScreenshot() async -> Bool {
let screenshot = await ioService?.screenshot()
@ -112,14 +76,14 @@ extension UTMQemuSpiceVirtualMachine {
}
// MARK: - External drives
extension UTMQemuSpiceVirtualMachine {
extension UTMSpiceVirtualMachine {
@MainActor func externalImageURL(for drive: UTMQemuConfigurationDrive) -> URL? {
registryEntry.externalDrives[drive.id]?.url
}
}
// MARK: - Shared directory
extension UTMQemuSpiceVirtualMachine {
extension UTMSpiceVirtualMachine {
@MainActor var sharedDirectoryURL: URL? {
registryEntry.sharedDirectories.first?.url
}
@ -169,7 +133,7 @@ extension UTMQemuSpiceVirtualMachine {
}
// MARK: - Registry syncing
extension UTMQemuSpiceVirtualMachine {
extension UTMSpiceVirtualMachine {
@MainActor func updateRegistryFromConfig() async throws {
// save a copy to not collide with updateConfigFromRegistry()
let configShare = config.sharing.directoryShareUrl

View File

@ -632,7 +632,7 @@
CE6C13CB2B63610C003B7032 /* UTMRemoteMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6C13C92B63610C003B7032 /* UTMRemoteMessage.swift */; };
CE6D21DC2553A6ED001D29C5 /* VMConfirmActionModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6D21DB2553A6ED001D29C5 /* VMConfirmActionModifier.swift */; };
CE6D21DD2553A6ED001D29C5 /* VMConfirmActionModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE6D21DB2553A6ED001D29C5 /* VMConfirmActionModifier.swift */; };
CE70E8D52B648FBE007FA787 /* UTMRemoteQemuVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE70E8D42B648FBE007FA787 /* UTMRemoteQemuVirtualMachine.swift */; };
CE70E8D52B648FBE007FA787 /* UTMRemoteSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE70E8D42B648FBE007FA787 /* UTMRemoteSpiceVirtualMachine.swift */; };
CE772AAC25C8B0F600E4E379 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE772AAB25C8B0F600E4E379 /* ContentView.swift */; };
CE772AAD25C8B0F600E4E379 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE772AAB25C8B0F600E4E379 /* ContentView.swift */; };
CE772AB325C8B7B500E4E379 /* VMCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE772AB225C8B7B500E4E379 /* VMCommands.swift */; };
@ -893,10 +893,10 @@
CEE7E938287CFDB100282049 /* UTMLegacyQemuConfiguration+Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = CEE7E934287CFDB100282049 /* UTMLegacyQemuConfiguration+Constants.m */; };
CEEC811B24E48EC700ACB0B3 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC811A24E48EC600ACB0B3 /* SettingsView.swift */; };
CEECE13C25E47D9500A2AAB8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEECE13B25E47D9500A2AAB8 /* AppDelegate.swift */; };
CEF01DB22B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift */; };
CEF01DB32B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift */; };
CEF01DB42B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift */; };
CEF01DB52B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift */; };
CEF01DB22B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMSpiceVirtualMachine.swift */; };
CEF01DB32B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMSpiceVirtualMachine.swift */; };
CEF01DB42B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMSpiceVirtualMachine.swift */; };
CEF01DB52B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF01DB12B6724A300725A0F /* UTMSpiceVirtualMachine.swift */; };
CEF0300826A25A6900667B63 /* VMWizardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF0300526A25A6900667B63 /* VMWizardView.swift */; };
CEF0304E26A2AFBE00667B63 /* BigButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF0304C26A2AFBE00667B63 /* BigButtonStyle.swift */; };
CEF0304F26A2AFBF00667B63 /* BigButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF0304C26A2AFBE00667B63 /* BigButtonStyle.swift */; };
@ -1910,7 +1910,7 @@
CE6EDCDE241C4A6800A719DC /* UTMLegacyViewState.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UTMLegacyViewState.m; sourceTree = "<group>"; };
CE6EDCE0241DA0E900A719DC /* UTMLogging.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UTMLogging.h; sourceTree = "<group>"; };
CE6EDCE1241DA0E900A719DC /* UTMLogging.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UTMLogging.m; sourceTree = "<group>"; };
CE70E8D42B648FBE007FA787 /* UTMRemoteQemuVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMRemoteQemuVirtualMachine.swift; sourceTree = "<group>"; };
CE70E8D42B648FBE007FA787 /* UTMRemoteSpiceVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMRemoteSpiceVirtualMachine.swift; sourceTree = "<group>"; };
CE72B4AB2463579D00716A11 /* VMDisplayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VMDisplayViewController.h; sourceTree = "<group>"; };
CE72B4AC2463579D00716A11 /* VMDisplayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VMDisplayViewController.m; sourceTree = "<group>"; };
CE772AAB25C8B0F600E4E379 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@ -2009,7 +2009,7 @@
CEEB66452284B942002737B2 /* VMKeyboardButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VMKeyboardButton.m; sourceTree = "<group>"; };
CEEC811A24E48EC600ACB0B3 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
CEECE13B25E47D9500A2AAB8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
CEF01DB12B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMQemuSpiceVirtualMachine.swift; sourceTree = "<group>"; };
CEF01DB12B6724A300725A0F /* UTMSpiceVirtualMachine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTMSpiceVirtualMachine.swift; sourceTree = "<group>"; };
CEF0300526A25A6900667B63 /* VMWizardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMWizardView.swift; sourceTree = "<group>"; };
CEF0304C26A2AFBE00667B63 /* BigButtonStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BigButtonStyle.swift; sourceTree = "<group>"; };
CEF0304D26A2AFBE00667B63 /* Spinner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Spinner.swift; sourceTree = "<group>"; };
@ -2780,7 +2780,7 @@
CE020BB524B14F8400B44AB6 /* UTMVirtualMachine.swift */,
CE928C2926ABE6690099F293 /* UTMAppleVirtualMachine.swift */,
841E999728AC817D003C6CB6 /* UTMQemuVirtualMachine.swift */,
CEF01DB12B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift */,
CEF01DB12B6724A300725A0F /* UTMSpiceVirtualMachine.swift */,
);
path = Services;
sourceTree = "<group>";
@ -2849,7 +2849,7 @@
CE9B15402B11A74E003A32DD /* UTMRemoteKeyManager.swift */,
CE38EC682B5DB3AE008B324B /* UTMRemoteClient.swift */,
CE6C13C92B63610C003B7032 /* UTMRemoteMessage.swift */,
CE70E8D42B648FBE007FA787 /* UTMRemoteQemuVirtualMachine.swift */,
CE70E8D42B648FBE007FA787 /* UTMRemoteSpiceVirtualMachine.swift */,
CE9B153E2B11A63E003A32DD /* UTMRemoteServer.swift */,
CE9B15452B12A87E003A32DD /* GenerateKey.h */,
CE9B15462B12A87E003A32DD /* GenerateKey.c */,
@ -3464,7 +3464,7 @@
CEF0305126A2AFBF00667B63 /* Spinner.swift in Sources */,
843BF840284555E70029D60D /* UTMQemuConfigurationPortForward.swift in Sources */,
CE611BE729F50CAD001817BC /* UTMReleaseHelper.swift in Sources */,
CEF01DB22B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */,
CEF01DB22B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */,
CE2D958324AD4F990059923A /* VMConfigNetworkView.swift in Sources */,
CE2D929C24AD46670059923A /* UTMLegacyViewState.m in Sources */,
CE020BAB24AEE00000B44AB6 /* UTMLoggingSwift.swift in Sources */,
@ -3712,7 +3712,7 @@
CEC0A30A2A7490D200980857 /* VMConfigQEMUArgumentsView.swift in Sources */,
843232B928C4816100CFBC97 /* UTMDownloadSupportToolsTask.swift in Sources */,
8471772A27CD3CAB00D3A50B /* DetailedSection.swift in Sources */,
CEF01DB52B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */,
CEF01DB52B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */,
8432329A28C3084A00CFBC97 /* GlobalFileImporter.swift in Sources */,
CE19392826DCB094005CEC17 /* RAMSlider.swift in Sources */,
2C33B3AA2566C9B100A954A6 /* VMContextMenuModifier.swift in Sources */,
@ -3873,7 +3873,7 @@
CEF0305226A2AFBF00667B63 /* Spinner.swift in Sources */,
CEA45EBD263519B5002FA97D /* UTMQemuSystem.m in Sources */,
CEA45EBE263519B5002FA97D /* NumberTextField.swift in Sources */,
CEF01DB32B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */,
CEF01DB32B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */,
843232B828C4816100CFBC97 /* UTMDownloadSupportToolsTask.swift in Sources */,
CEF0306526A2AFDF00667B63 /* VMWizardOSLinuxView.swift in Sources */,
CEA45EC3263519B5002FA97D /* VMCommands.swift in Sources */,
@ -3962,7 +3962,7 @@
CEF7F59E2AEEDCC400E34952 /* UTMLegacyQemuConfiguration+System.m in Sources */,
CEF7F59F2AEEDCC400E34952 /* UTMQemuConfigurationNetwork.swift in Sources */,
CEF7F5A02AEEDCC400E34952 /* UTMProcess.m in Sources */,
CE70E8D52B648FBE007FA787 /* UTMRemoteQemuVirtualMachine.swift in Sources */,
CE70E8D52B648FBE007FA787 /* UTMRemoteSpiceVirtualMachine.swift in Sources */,
CEF7F5A12AEEDCC400E34952 /* VMWizardDrivesView.swift in Sources */,
CEF7F5A22AEEDCC400E34952 /* VMWindowState.swift in Sources */,
CEF7F5A42AEEDCC400E34952 /* UTMLegacyQemuConfigurationPortForward.m in Sources */,
@ -4045,7 +4045,7 @@
CEF7F5EF2AEEDCC400E34952 /* UTMQemuSystem.m in Sources */,
CEF7F5F02AEEDCC400E34952 /* NumberTextField.swift in Sources */,
CEF7F5F12AEEDCC400E34952 /* VMToolbarOrnamentModifier.swift in Sources */,
CEF01DB42B6724A300725A0F /* UTMQemuSpiceVirtualMachine.swift in Sources */,
CEF01DB42B6724A300725A0F /* UTMSpiceVirtualMachine.swift in Sources */,
CEF7F5F22AEEDCC400E34952 /* VMCommands.swift in Sources */,
CEF7F5F32AEEDCC400E34952 /* UTMLegacyQemuConfiguration+Networking.m in Sources */,
CEF7F5F42AEEDCC400E34952 /* VMConfirmActionModifier.swift in Sources */,