diff --git a/Configuration/UTMQemuConfigurationQEMU.swift b/Configuration/UTMQemuConfigurationQEMU.swift
index 14c6d2619..8c64e9069 100644
--- a/Configuration/UTMQemuConfigurationQEMU.swift
+++ b/Configuration/UTMQemuConfigurationQEMU.swift
@@ -66,6 +66,8 @@ struct UTMQemuConfigurationQEMU: Codable {
/// Set to true to request guest tools install. Not saved.
var isGuestToolsInstallRequested: Bool = false
+ var unattended: Bool = false
+
/// Set to true to request UEFI variable reset. Not saved.
var isUefiVariableResetRequested: Bool = false
diff --git a/Platform/Shared/VMContextMenuModifier.swift b/Platform/Shared/VMContextMenuModifier.swift
index 066f5dfd1..3e6666e41 100644
--- a/Platform/Shared/VMContextMenuModifier.swift
+++ b/Platform/Shared/VMContextMenuModifier.swift
@@ -179,7 +179,8 @@ struct VMContextMenuModifier: ViewModifier {
.onChange(of: (vm.config as? UTMQemuConfiguration)?.qemu.isGuestToolsInstallRequested) { newValue in
if newValue == true {
data.busyWorkAsync {
- try await data.mountSupportTools(for: vm.wrapped!)
+ let unattend = await (vm.config as? UTMQemuConfiguration)?.qemu.unattended ?? false
+ try await data.mountSupportTools(for: vm.wrapped!, unattendless: unattend)
}
}
}
diff --git a/Platform/Shared/VMWizardOSWindowsView.swift b/Platform/Shared/VMWizardOSWindowsView.swift
index 2136123e9..9dee322fa 100644
--- a/Platform/Shared/VMWizardOSWindowsView.swift
+++ b/Platform/Shared/VMWizardOSWindowsView.swift
@@ -60,6 +60,7 @@ struct VMWizardOSWindowsView: View {
} label: {
Label("Fetch latest Windows installer…", systemImage: "link")
}.buttonStyle(.link)
+ Toggle("Unattended Installation", isOn: $wizardState.windowsUnattendedInstall)
}
#endif
Link(destination: URL(string: "https://docs.getutm.app/guides/windows/")!) {
diff --git a/Platform/Shared/VMWizardState.swift b/Platform/Shared/VMWizardState.swift
index 48d9dbfc2..f8a34baeb 100644
--- a/Platform/Shared/VMWizardState.swift
+++ b/Platform/Shared/VMWizardState.swift
@@ -30,6 +30,7 @@ enum VMWizardPage: Int, Identifiable {
case macOSBoot
case linuxBoot
case windowsBoot
+ case windowsUnattendConfig
case otherBoot
case hardware
case drives
@@ -136,6 +137,10 @@ enum VMBootDevice: Int, Identifiable {
@Published var name: String?
@Published var isOpenSettingsAfterCreation: Bool = false
@Published var useNvmeAsDiskInterface = false
+ @Published var windowsUnattendedInstall = false
+ @Published var unattendLanguage = "en-US"
+ @Published var unattendUsername = "user"
+ @Published var unattendPassword = ""
/// SwiftUI BUG: on macOS 12, when VoiceOver is enabled and isBusy changes the disable state of a button being clicked,
var isNeverDisabledWorkaround: Bool {
@@ -239,6 +244,12 @@ enum VMBootDevice: Int, Identifiable {
alertMessage = AlertMessage(NSLocalizedString("Please select a boot image.", comment: "VMWizardState"))
return
}
+ if windowsUnattendedInstall {
+ nextPage = .windowsUnattendConfig
+ } else {
+ nextPage = .hardware
+ }
+ case .windowsUnattendConfig:
nextPage = .hardware
case .hardware:
guard systemMemoryMib > 0 else {
@@ -512,9 +523,19 @@ enum VMBootDevice: Int, Identifiable {
diskImage.imageType = .disk
diskImage.interface = mainDriveInterface
config.drives.append(diskImage)
- if operatingSystem == .Windows && isGuestToolsInstallRequested {
- let toolsDiskDrive = UTMQemuConfigurationDrive(forArchitecture: systemArchitecture, target: systemTarget, isExternal: true)
- config.drives.append(toolsDiskDrive)
+ if operatingSystem == .Windows {
+ if isGuestToolsInstallRequested {
+ let toolsDiskDrive = UTMQemuConfigurationDrive(forArchitecture: systemArchitecture, target: systemTarget, isExternal: true)
+ config.drives.append(toolsDiskDrive)
+ }
+ if windowsUnattendedInstall {
+ var unattendDrive = UTMQemuConfigurationDrive(forArchitecture: systemArchitecture, target: systemTarget, isExternal: false)
+ unattendDrive.isRawImage = true
+ unattendDrive.imageURL = try createAutounattendIso()
+ unattendDrive.interface = .usb
+ unattendDrive.imageType = .cd
+ config.drives.append(unattendDrive)
+ }
}
}
if legacyHardware {
@@ -524,6 +545,520 @@ enum VMBootDevice: Int, Identifiable {
return config
}
+ func createUnattendXml() -> String {
+ return """
+
+
+
+
+
+ false
+
+
+ false
+
+
+ *
+
+
+ *
+
+
+
+
+ true
+
+
+ true
+
+
+ 1
+
+
+ 1
+
+
+
+
+ true
+
+
+ true
+
+
+ *
+
+ UTM
+ UTM Virtual Machine
+
+ UTM
+ https://mac.getutm.app/support/
+
+ UTM
+
+
+ *
+
+ UTM
+ UTM Virtual Machine
+
+ UTM
+ https://mac.getutm.app/support/
+
+ UTM
+
+
+ 0
+
+
+ 0
+
+
+
+
+
+ \(unattendLanguage)
+
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+
+
+
+ \(unattendLanguage)
+
+ e\(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+
+
+
+ false
+
+
+
+ 1
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassCPUCheck /t REG_DWORD /d 0x00000001 /f
+
+
+ 2
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassRAMCheck /t REG_DWORD /d 0x00000001 /f
+
+
+ 3
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 0x00000001 /f
+
+
+ 4
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassTPMCheck /t REG_DWORD /d 0x00000001 /f
+
+
+
+
+
+ W269N-WFGWX-YVC9B-4J6C9-T83GX
+
+ true
+
+
+
+
+
+ 1
+ 100
+ EFI
+
+
+ 2
+ 16
+ MSR
+
+
+ 3
+ Primary
+ true
+
+
+
+
+ FAT32
+
+ 1
+ 1
+
+
+ NTFS
+ 2
+ 3
+
+
+ 0
+ true
+
+ OnError
+
+
+
+
+ 0
+ 3
+
+
+
+
+
+
+ false
+
+
+
+ 1
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassCPUCheck /t REG_DWORD /d 0x00000001 /f
+
+
+ 2
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassRAMCheck /t REG_DWORD /d 0x00000001 /f
+
+
+ 3
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassSecureBootCheck /t REG_DWORD /d 0x00000001 /f
+
+
+ 4
+ reg add HKLM\\System\\Setup\\LabConfig /v BypassTPMCheck /t REG_DWORD /d 0x00000001 /f
+
+
+
+
+
+ W269N-WFGWX-YVC9B-4J6C9-T83GX
+
+ true
+
+
+
+
+
+ 1
+ 100
+ EFI
+
+
+ 2
+ 16
+ MSR
+
+
+ 3
+ Primary
+ true
+
+
+
+
+ FAT32
+
+ 1
+ 1
+
+
+ NTFS
+ 2
+ 3
+
+
+ 0
+ true
+
+ OnError
+
+
+
+
+ 0
+ 3
+
+
+
+
+
+
+
+
+ E:\\Drivers\\qemufwcfg\\w10\\amd64
+
+
+ E:\\Drivers\\vioscsi\\w10\\amd64
+
+
+ E:\\Drivers\\viostor\\w10\\amd64
+
+
+ E:\\Drivers\\vioserial\\w10\\amd64
+
+
+ E:\\Drivers\\qxldod\\w10\\amd64
+
+
+ E:\\Drivers\\viogpu\\w10\\amd64
+
+
+ E:\\Drivers\\viorng\\w10\\amd64
+
+
+ E:\\Drivers\\NetKVM\\w10\\amd64
+
+
+ E:\\Drivers\\Balloon\\w10\\amd64
+
+
+
+
+
+
+ E:\\Drivers\\vioscsi\\w10\\ARM64
+
+
+ E:\\Drivers\\viostor\\w10\\ARM64
+
+
+ E:\\Drivers\\vioserial\\w10\\ARM64
+
+
+ E:\\Drivers\\viogpu\\w10\\ARM64
+
+
+ E:\\Drivers\\NetKVM\\w10\\ARM64
+
+
+ E:\\Drivers\\Balloon\\w10\\ARM64
+
+
+
+
+
+
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+
+
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+ \(unattendLanguage)
+
+
+
+
+
+ \(unattendUsername)
+ \(unattendUsername)
+ Administrators
+
+ \(unattendPassword)
+ true
+
+
+
+
+
+ true
+ \(unattendUsername)
+
+ true
+ \(unattendPassword)
+
+ 1
+
+
+ true
+ true
+ true
+ 3
+
+ true
+
+
+
+
+ Cmd /c POWERCFG -H OFF
+ Disable Hibernation
+ 1
+
+
+ reg add "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" /v AutoLogonCount /t REG_DWORD /d 0 /f
+ Disable Autologon
+ 2
+
+
+ powershell "$name = 'utm-guest-tools-0.229.exe'; foreach ($drive in Get-PSDrive -PSProvider FileSystem) { $path = Join-Path $drive.Root $name; if (Test-Path $path) { & $path; break } }"
+ Install SPICE tools
+ 3
+
+
+
+
+
+
+
+ alice
+ \(unattendUsername)
+ Administrators
+
+ \(unattendPassword)
+ true
+
+
+
+
+
+ true
+ \(unattendUsername)
+
+ true
+ \(unattendPassword)
+
+ 1
+
+
+ true
+ true
+ true
+ 3
+
+ true
+
+
+
+
+ Cmd /c POWERCFG -H OFF
+ Disable Hibernation
+ 1
+
+
+ reg add "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon" /v AutoLogonCount /t REG_DWORD /d 0 /f
+ Disable Autologon
+ 2
+
+
+ powershell "$name = 'utm-guest-tools-0.229.exe'; foreach ($drive in Get-PSDrive -PSProvider FileSystem) { $path = Join-Path $drive.Root $name; if (Test-Path $path) { & $path; break } }"
+ Install SPICE tools
+ 3
+
+
+
+
+
+"""
+ }
+
+ func createAutounattendIso() throws -> URL {
+ let fileManager = FileManager.default
+ let url = fileManager.temporaryDirectory.appendingPathComponent("autounattend.iso")
+ let xml = createUnattendXml().data(using: .utf8)!
+ // Data layout
+ // Sectors 0-15 - empty
+ // Sector 16 - Primary Volume Descriptor
+ // Sector 17 - Descriptor Set Terminator
+ // Sector 18 - LE Path Table
+ // Sector 19 - BE Path Table
+ // Sector 20 - Root Directory
+ // Sector 21 - File Data
+ let sector = 2048
+ var iso = Data(count: sector * 21)
+ let xpad = 100
+ iso.withCursor { data in
+ var data = data
+ let descriptorId = "CD001"
+ data.advance(by: 16 * sector)
+ data.write(u8: 1) // Primary Volume Descriptor
+ data.write(ascii: descriptorId) // id
+ data.write(u8: 1) // version
+ data.write(u8: 0) // unused
+ data.write(ascii: "", padTo: 32) // System Identifier
+ data.write(ascii: "AUTOUNATTEND", padTo: 32) // Volume Identifier
+ data.advance(by: 8)
+ var xmlSect = xml.count / sector
+ if xml.count % sector != 0 {
+ xmlSect += 1
+ }
+ data.write(i32bi: Int32(20 + xmlSect + xpad)) // Volume Space Size
+ data.advance(by: 32)
+ data.write(i16bi: 1) // Volume Set Size
+ data.write(i16bi: 1) // Volume Sequence Number
+ data.write(i16bi: Int16(sector)) // Logical Block Size
+ data.write(i32bi: 10) // Path table size
+ data.write(i32: 18) // Type-L Path Table
+ data.write(i32: 0) // Type-L Optional Path Table
+ data.write(i32: Int32(19).bigEndian) // Type-M Path Table
+ data.write(i32: 0) // Type-M Optional Path Table
+ // Root Directory Record
+ data.writeDirRecord(name: Data(repeating: 0, count: 1), location: 20, size: Int32(sector), directory: true)
+
+ data.write(ascii: "", padTo: 128) // Volume Set Identifier
+ data.write(ascii: "", padTo: 128) // Publisher Identifier
+ data.write(ascii: "", padTo: 128) // Data Preparer Identifier
+ data.write(ascii: "", padTo: 128) // Application Identifier
+ data.write(ascii: "", padTo: 37) // Copyright File Identifier
+ data.write(ascii: "", padTo: 37) // Abstract File Identifier
+ data.write(ascii: "", padTo: 37) // Bibliographic File Identifier
+ data.write(decDate: NSDate.now as Date) // Creation Date
+ data.write(decDate: NSDate.now as Date) // Modification Date
+ data.write(decDate: NSDate.now as Date + 315360000) // Expiration Date
+ data.write(decDate: NSDate.now as Date) // Effective Date
+ data.write(u8: 1) // File Structure Version
+ data.write(u8: 0) // Padding
+ data.advance(by: 512) // Application Use
+ data.advance(by: 653) // Reserved
+
+ data.write(u8: 255) // Descriptor Set Terminator
+ data.write(ascii: descriptorId) // id
+ data.write(u8: 1) // Version
+ data.advance(by: sector - 7) // Reserved
+
+ // L - Path Table
+ data.write(u8: 1) // Directory Identifier Length
+ data.write(u8: 0) // XA Length
+ data.write(i32: 20) // Location
+ data.write(i16: 1) // Parent directory number
+ data.write(u8: 0) // Filename
+ data.write(u8: 0) // Padding
+ data.advance(by: sector - 10)
+
+ // M - Path Table
+ data.write(u8: 1) // Directory Identifier Length
+ data.write(u8: 0) // XA Length
+ data.write(i32: Int32(20).bigEndian) // Location
+ data.write(i16: Int16(1).bigEndian) // Parent directory number
+ data.write(u8: 0) // Filename
+ data.write(u8: 0) // Padding
+ data.advance(by: sector - 10)
+
+ // Root directory
+ data.writeDirRecord(name: Data(repeating: 0, count: 1), location: 20, size: Int32(sector), directory: true)
+ data.writeDirRecord(name: Data(repeating: 1, count: 1), location: 20, size: Int32(sector), directory: true)
+ data.writeDirRecord(name: "AUTOUNATTEND.XML;1".data(using: .ascii)!, location: 21, size: Int32(xml.count), directory: false)
+ }
+ iso.append(xml)
+ if xml.count % sector != 0 {
+ iso.append(Data(repeating: 0, count: 2048 - xml.count % sector))
+ }
+ iso.append(Data(repeating: 0, count: sector * xpad))
+ fileManager.createFile(atPath: url.path, contents: iso)
+ return url
+ }
+
func generateConfig() throws -> any UTMConfiguration {
if useVirtualization && useAppleVirtualization {
#if os(macOS)
@@ -552,6 +1087,126 @@ enum VMBootDevice: Int, Identifiable {
}
}
+struct DataCursor {
+ var ptr: UnsafeMutableRawBufferPointer
+ var cursor: Int
+ mutating func advance(by: Int) {
+ self.cursor += by
+ }
+ mutating func write(u8: UInt8) {
+ ptr[cursor] = u8
+ self.cursor += 1
+ }
+ mutating func write(i32: Int32) {
+ ptr.storeBytes(of: i32, toByteOffset: self.cursor, as: Int32.self)
+ self.cursor += 4
+ }
+ mutating func write(i32bi i: Int32) {
+ write(i32: i)
+ write(i32: i.bigEndian)
+ }
+ mutating func write(i16: Int16) {
+ ptr.storeBytes(of: i16, toByteOffset: self.cursor, as: Int16.self)
+ self.cursor += 2
+ }
+ mutating func write(i16bi i: Int16) {
+ write(i16: i)
+ write(i16: i.bigEndian)
+ }
+ mutating func write(ascii: String) {
+ write(ascii: ascii, padTo: ascii.utf8.count)
+ }
+ mutating func write(ascii: String, padTo: Int) {
+ ascii.withCString { bytes in
+ for i in 0..(_ body: (DataCursor) throws -> ResultType) rethrows -> ResultType {
+ try self.withUnsafeMutableBytes { ptr in
+ let dc = DataCursor(ptr: ptr, cursor: 0)
+ return try body(dc)
+ }
+ }
+}
+
// MARK: - Warnings for common mistakes
extension VMWizardState {
diff --git a/Platform/Shared/VMWizardWindowsUnattendView.swift b/Platform/Shared/VMWizardWindowsUnattendView.swift
new file mode 100644
index 000000000..367bc3443
--- /dev/null
+++ b/Platform/Shared/VMWizardWindowsUnattendView.swift
@@ -0,0 +1,48 @@
+//
+// Copyright © 2021 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 SwiftUI
+
+struct VMWizardWindowsUnattendView: View {
+ @ObservedObject var wizardState: VMWizardState
+ var body: some View {
+ VMWizardContent("Unattended Installation") {
+ Section {
+ Form {
+ TextField("Language", text: $wizardState.unattendLanguage)
+ .keyboardType(.asciiCapable)
+ .lineLimit(1)
+ TextField("Username", text: $wizardState.unattendUsername)
+ .keyboardType(.asciiCapable)
+ .lineLimit(1)
+ SecureField("Password", text: $wizardState.unattendPassword)
+ .keyboardType(.asciiCapable)
+ .lineLimit(1)
+ }
+ } header: {
+ Text("Configuration")
+ }
+ }
+ }
+}
+
+struct VMWizardWindowsUnattendView_Previews: PreviewProvider {
+ @StateObject static var wizardState = VMWizardState()
+
+ static var previews: some View {
+ VMWizardWindowsUnattendView(wizardState: wizardState)
+ }
+}
diff --git a/Platform/UTMData.swift b/Platform/UTMData.swift
index 5af4ccca2..bfeeb5a7b 100644
--- a/Platform/UTMData.swift
+++ b/Platform/UTMData.swift
@@ -737,11 +737,11 @@ struct AlertMessage: Identifiable {
}
}
- func mountSupportTools(for vm: any UTMVirtualMachine) async throws {
+ func mountSupportTools(for vm: any UTMVirtualMachine, unattendless: Bool) async throws {
guard let vm = vm as? any UTMSpiceVirtualMachine else {
throw UTMDataError.unsupportedBackend
}
- let task = UTMDownloadSupportToolsTask(for: vm)
+ let task = UTMDownloadSupportToolsTask(for: vm, unattendless: unattendless)
if await task.hasExistingSupportTools {
vm.config.qemu.isGuestToolsInstallRequested = false
_ = try await task.mountTools()
diff --git a/Platform/UTMDownloadSupportToolsTask.swift b/Platform/UTMDownloadSupportToolsTask.swift
index c7dabfecb..1848265fd 100644
--- a/Platform/UTMDownloadSupportToolsTask.swift
+++ b/Platform/UTMDownloadSupportToolsTask.swift
@@ -19,15 +19,21 @@ import Foundation
/// Downloads support tools ISO
class UTMDownloadSupportToolsTask: UTMDownloadTask {
private let vm: any UTMSpiceVirtualMachine
+ private let unattend: Bool
private static let supportToolsDownloadUrl = URL(string: "https://getutm.app/downloads/utm-guest-tools-latest.iso")!
+ private static let supportToolsDownloadUrlUa = URL(string: "https://getutm.app/downloads/unattendless.iso")!
private var toolsUrl: URL {
fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!.appendingPathComponent("GuestSupportTools")
}
private var supportToolsLocalUrl: URL {
- toolsUrl.appendingPathComponent(Self.supportToolsDownloadUrl.lastPathComponent)
+ var url = Self.supportToolsDownloadUrl
+ if unattend {
+ url = Self.supportToolsDownloadUrlUa
+ }
+ return toolsUrl.appendingPathComponent(url.lastPathComponent)
}
@Setting("LastDownloadedGuestTools")
@@ -42,10 +48,15 @@ class UTMDownloadSupportToolsTask: UTMDownloadTask {
}
}
- init(for vm: any UTMSpiceVirtualMachine) {
+ init(for vm: any UTMSpiceVirtualMachine, unattendless: Bool) {
self.vm = vm
let name = NSLocalizedString("Windows Guest Support Tools", comment: "UTMDownloadSupportToolsTask")
- super.init(for: Self.supportToolsDownloadUrl, named: name)
+ self.unattend = unattendless
+ var url = Self.supportToolsDownloadUrl
+ if unattend {
+ url = Self.supportToolsDownloadUrlUa
+ }
+ super.init(for: url, named: name)
}
override func processCompletedDownload(at location: URL, response: URLResponse?) async throws -> any UTMVirtualMachine {
diff --git a/Platform/iOS/VMWizardView.swift b/Platform/iOS/VMWizardView.swift
index 266c28eeb..58e8e40d9 100644
--- a/Platform/iOS/VMWizardView.swift
+++ b/Platform/iOS/VMWizardView.swift
@@ -173,6 +173,8 @@ fileprivate struct WizardViewWrapper: View {
VMWizardOSView(wizardState: wizardState)
case .macOSBoot:
EmptyView()
+ case .windowsUnattendConfig:
+ VMWizardWindowsUnattendView(wizardState: wizardState)
case .linuxBoot:
VMWizardOSLinuxView(wizardState: wizardState)
case .windowsBoot:
diff --git a/Platform/macOS/VMWizardView.swift b/Platform/macOS/VMWizardView.swift
index 380d3f733..d17b50bcf 100644
--- a/Platform/macOS/VMWizardView.swift
+++ b/Platform/macOS/VMWizardView.swift
@@ -60,6 +60,9 @@ struct VMWizardView: View {
case .windowsBoot:
VMWizardOSWindowsView(wizardState: wizardState)
.transition(wizardState.slide)
+ case .windowsUnattendConfig:
+ VMWizardWindowsUnattendView(wizardState: wizardState)
+ .transition(wizardState.slide)
case .hardware:
VMWizardHardwareView(wizardState: wizardState)
.transition(wizardState.slide)
@@ -115,6 +118,7 @@ struct VMWizardView: View {
_ = try await data.create(config: qemuConfig)
await MainActor.run {
qemuConfig.qemu.isGuestToolsInstallRequested = wizardState.isGuestToolsInstallRequested
+ qemuConfig.qemu.unattended = wizardState.windowsUnattendedInstall
}
} else if let appleConfig = config as? UTMAppleConfiguration {
_ = try await data.create(config: appleConfig)
diff --git a/Remote/UTMRemoteServer.swift b/Remote/UTMRemoteServer.swift
index 7a50dede6..0a75e37ed 100644
--- a/Remote/UTMRemoteServer.swift
+++ b/Remote/UTMRemoteServer.swift
@@ -812,7 +812,7 @@ extension UTMRemoteServer {
private func _mountGuestToolsOnVirtualMachine(parameters: M.MountGuestToolsOnVirtualMachine.Request) async throws -> M.MountGuestToolsOnVirtualMachine.Reply {
let vm = try await findVM(withId: parameters.id)
if let wrapped = await vm.wrapped {
- try await data.mountSupportTools(for: wrapped)
+ try await data.mountSupportTools(for: wrapped, unattendless: false)
}
return .init()
}
diff --git a/UTM.xcodeproj/project.pbxproj b/UTM.xcodeproj/project.pbxproj
index 4603c8241..cb46d6402 100644
--- a/UTM.xcodeproj/project.pbxproj
+++ b/UTM.xcodeproj/project.pbxproj
@@ -7,6 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
+ 003B61A42C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003B61A32C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift */; };
+ 003B61A52C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003B61A32C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift */; };
+ 003B61A62C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003B61A32C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift */; };
+ 003B61A72C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003B61A32C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift */; };
2C33B3A92566C9B100A954A6 /* VMContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C33B3A82566C9B100A954A6 /* VMContextMenuModifier.swift */; };
2C33B3AA2566C9B100A954A6 /* VMContextMenuModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C33B3A82566C9B100A954A6 /* VMContextMenuModifier.swift */; };
2C6D9E03256EE454003298E6 /* VMDisplayQemuTerminalWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6D9E02256EE454003298E6 /* VMDisplayQemuTerminalWindowController.swift */; };
@@ -1584,6 +1588,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 003B61A32C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VMWizardWindowsUnattendView.swift; sourceTree = ""; };
037DAA1C2B0B92580061ACB3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/VMDisplayWindow.strings; sourceTree = ""; };
037DAA1D2B0B92580061ACB3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; };
037DAA1E2B0B92580061ACB3 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; };
@@ -2972,6 +2977,7 @@
83034C0626AB630F006B4BAF /* UTMPendingVMView.swift */,
84909A8C27CACD5C005605F1 /* UTMPlaceholderVMView.swift */,
84909A9027CADAE0005605F1 /* UTMUnavailableVMView.swift */,
+ 003B61A32C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift */,
);
path = Shared;
sourceTree = "";
@@ -3605,6 +3611,7 @@
CEF0307426A2B40B00667B63 /* VMWizardHardwareView.swift in Sources */,
841E997528AA1191003C6CB6 /* UTMRegistry.swift in Sources */,
8401868F288A50B90050AC51 /* VMDisplayViewControllerDelegate.swift in Sources */,
+ 003B61A42C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */,
CE2D92D724AD46670059923A /* UTMLogging.m in Sources */,
848D99A8285DB5550055C215 /* VMConfigConstantPicker.swift in Sources */,
8453DCB4278CE5410037A0DA /* UTMQemuImage.swift in Sources */,
@@ -3787,6 +3794,7 @@
CE0B6CF524AD568400FE012D /* UTMLegacyQemuConfiguration+Miscellaneous.m in Sources */,
CE25125129C806AF000790AB /* UTMScriptingDeleteCommand.swift in Sources */,
CE0B6CFB24AD568400FE012D /* UTMLegacyQemuConfiguration+Networking.m in Sources */,
+ 003B61A72C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */,
84C584E5268F8C65000FCABF /* VMAppleSettingsView.swift in Sources */,
CE9B15442B11A74E003A32DD /* UTMRemoteKeyManager.swift in Sources */,
84F746BB276FF70700A20C87 /* VMDisplayQemuDisplayController.swift in Sources */,
@@ -4021,6 +4029,7 @@
CEA45F00263519B5002FA97D /* VMCardView.swift in Sources */,
CEA45F01263519B5002FA97D /* UTMLegacyQemuConfiguration+Sharing.m in Sources */,
841619B328431DA5000034B2 /* UTMQemuConfigurationQEMU.swift in Sources */,
+ 003B61A52C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */,
CEF0305C26A2AFDF00667B63 /* VMWizardOSOtherView.swift in Sources */,
CEA45F08263519B5002FA97D /* ActivityView.swift in Sources */,
848F71E9277A2A4E006A0240 /* UTMSerialPort.swift in Sources */,
@@ -4138,6 +4147,7 @@
CEF7F5E82AEEDCC400E34952 /* VMDisplayViewControllerDelegate.swift in Sources */,
CEF7F5EA2AEEDCC400E34952 /* VMConfigConstantPicker.swift in Sources */,
CEF7F5EC2AEEDCC400E34952 /* VMToolbarModifier.swift in Sources */,
+ 003B61A62C03EC5B0013BA74 /* VMWizardWindowsUnattendView.swift in Sources */,
CEF7F5ED2AEEDCC400E34952 /* VMCursor.m in Sources */,
CEF7F5EE2AEEDCC400E34952 /* VMConfigDriveDetailsView.swift in Sources */,
CEF7F5F02AEEDCC400E34952 /* NumberTextField.swift in Sources */,