|
@@ -20,20 +20,28 @@ import Virtualization
|
|
#endif
|
|
#endif
|
|
|
|
|
|
struct VMWizardHardwareView: View {
|
|
struct VMWizardHardwareView: View {
|
|
- private enum ClassicMacSystem: CaseIterable, Identifiable {
|
|
|
|
|
|
+ private enum SupportedMachine: CaseIterable, Identifiable {
|
|
case quadra800
|
|
case quadra800
|
|
//case powerMacG3Beige
|
|
//case powerMacG3Beige
|
|
case powerMacG4
|
|
case powerMacG4
|
|
//case powerMacG5
|
|
//case powerMacG5
|
|
|
|
+ case i440FX
|
|
|
|
+ case q35
|
|
|
|
+ case arm64Virt
|
|
|
|
+ case riscv64Virt
|
|
|
|
|
|
var id: Self { self }
|
|
var id: Self { self }
|
|
|
|
|
|
var title: LocalizedStringKey {
|
|
var title: LocalizedStringKey {
|
|
switch self {
|
|
switch self {
|
|
- case .quadra800: "Macintosh Quadra 800 (M68K)"
|
|
|
|
- //case .powerMacG3Beige: "Power Macintosh G3 (Beige)"
|
|
|
|
- case .powerMacG4: "Power Macintosh G4 (PPC)"
|
|
|
|
- //case .powerMacG5: "Power Macintosh G5 (PPC64)"
|
|
|
|
|
|
+ case .quadra800: "Macintosh Quadra 800 (1993, M68K)"
|
|
|
|
+ //case .powerMacG3Beige: "Power Macintosh G3 (1997, Beige)"
|
|
|
|
+ case .powerMacG4: "Power Macintosh G4 (1999, PPC)"
|
|
|
|
+ //case .powerMacG5: "Power Macintosh G5 (2003, PPC64)"
|
|
|
|
+ case .i440FX: "Intel i440FX based PC (1996, x86_64)"
|
|
|
|
+ case .q35: "Intel ICH9 based PC (2009, x86_64)"
|
|
|
|
+ case .arm64Virt: "ARM64 virtual machine (2014, ARM64)"
|
|
|
|
+ case .riscv64Virt: "RISC-V64 virtual machine (2018, RISC-V64)"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -43,6 +51,10 @@ struct VMWizardHardwareView: View {
|
|
//case .powerMacG3Beige: return .ppc
|
|
//case .powerMacG3Beige: return .ppc
|
|
case .powerMacG4: return .ppc
|
|
case .powerMacG4: return .ppc
|
|
//case .powerMacG5: return .ppc64
|
|
//case .powerMacG5: return .ppc64
|
|
|
|
+ case .i440FX: return .x86_64
|
|
|
|
+ case .q35: return .x86_64
|
|
|
|
+ case .arm64Virt: return .aarch64
|
|
|
|
+ case .riscv64Virt: return .riscv64
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -52,6 +64,10 @@ struct VMWizardHardwareView: View {
|
|
//case .powerMacG3Beige: return QEMUTarget_ppc.g3beige
|
|
//case .powerMacG3Beige: return QEMUTarget_ppc.g3beige
|
|
case .powerMacG4: return QEMUTarget_ppc.mac99
|
|
case .powerMacG4: return QEMUTarget_ppc.mac99
|
|
//case .powerMacG5: return QEMUTarget_ppc.mac99
|
|
//case .powerMacG5: return QEMUTarget_ppc.mac99
|
|
|
|
+ case .i440FX: return QEMUTarget_x86_64.pc
|
|
|
|
+ case .q35: return QEMUTarget_x86_64.q35
|
|
|
|
+ case .arm64Virt: return QEMUTarget_aarch64.virt
|
|
|
|
+ case .riscv64Virt: return QEMUTarget_riscv64.virt
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -61,6 +77,7 @@ struct VMWizardHardwareView: View {
|
|
//case .powerMacG3Beige: return 32
|
|
//case .powerMacG3Beige: return 32
|
|
case .powerMacG4: return 64
|
|
case .powerMacG4: return 64
|
|
//case .powerMacG5: return 64
|
|
//case .powerMacG5: return 64
|
|
|
|
+ default: return 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -70,6 +87,7 @@ struct VMWizardHardwareView: View {
|
|
//case .powerMacG3Beige: return 2047
|
|
//case .powerMacG3Beige: return 2047
|
|
case .powerMacG4: return 2048
|
|
case .powerMacG4: return 2048
|
|
//case .powerMacG5: return 2048
|
|
//case .powerMacG5: return 2048
|
|
|
|
+ default: return 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -79,11 +97,62 @@ struct VMWizardHardwareView: View {
|
|
//case .powerMacG3Beige: return 512
|
|
//case .powerMacG3Beige: return 512
|
|
case .powerMacG4: return 512
|
|
case .powerMacG4: return 512
|
|
//case .powerMacG5: return 512
|
|
//case .powerMacG5: return 512
|
|
|
|
+ #if os(macOS)
|
|
|
|
+ default: return 4096
|
|
|
|
+ #else
|
|
|
|
+ default: return 512
|
|
|
|
+ #endif
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var defaultStorageGiB: Int {
|
|
|
|
+ switch self {
|
|
|
|
+ case .quadra800, .powerMacG4: return 2
|
|
|
|
+ #if os(macOS)
|
|
|
|
+ default: return 64
|
|
|
|
+ #else
|
|
|
|
+ default: return 2
|
|
|
|
+ #endif
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var maxSupportedCores: Int {
|
|
|
|
+ switch self {
|
|
|
|
+ case .quadra800, .powerMacG4: return 1
|
|
|
|
+ default: return 0
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ var isLegacyHardware: Bool {
|
|
|
|
+ switch self {
|
|
|
|
+ case .quadra800, .powerMacG4, .i440FX: return true
|
|
|
|
+ default: return false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ func isSupported(running os: VMWizardOS) -> Bool {
|
|
|
|
+ switch os {
|
|
|
|
+ case .Other: return true
|
|
|
|
+ case .macOS: return [.arm64Virt].contains(self)
|
|
|
|
+ case .Linux: return true
|
|
|
|
+ case .Windows: return [.i440FX, .q35, .arm64Virt].contains(self)
|
|
|
|
+ case .ClassicMacOS: return [.quadra800, .powerMacG4].contains(self)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ static func `default`(for os: VMWizardOS) -> Self {
|
|
|
|
+ switch os {
|
|
|
|
+ case .Other: return .q35
|
|
|
|
+ case .macOS: return .arm64Virt
|
|
|
|
+ case .Linux: return .q35
|
|
|
|
+ case .Windows: return .q35
|
|
|
|
+ case .ClassicMacOS: return .powerMacG4
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@ObservedObject var wizardState: VMWizardState
|
|
@ObservedObject var wizardState: VMWizardState
|
|
- @State private var classicMacSystem: ClassicMacSystem = .powerMacG4
|
|
|
|
|
|
+ @State private var isExpertMode: Bool = false
|
|
|
|
+ @State private var selectedMachine: SupportedMachine?
|
|
|
|
|
|
var minCores: Int {
|
|
var minCores: Int {
|
|
#if canImport(Virtualization)
|
|
#if canImport(Virtualization)
|
|
@@ -119,7 +188,11 @@ struct VMWizardHardwareView: View {
|
|
|
|
|
|
var body: some View {
|
|
var body: some View {
|
|
VMWizardContent("Hardware") {
|
|
VMWizardContent("Hardware") {
|
|
- if !wizardState.useVirtualization && wizardState.operatingSystem != .ClassicMacOS {
|
|
|
|
|
|
+ if !wizardState.useVirtualization {
|
|
|
|
+ Toggle("Expert Mode", isOn: $isExpertMode)
|
|
|
|
+ .help("List all supported hardware. May require manual configuration to boot.")
|
|
|
|
+ }
|
|
|
|
+ if !wizardState.useVirtualization && isExpertMode {
|
|
Section {
|
|
Section {
|
|
VMConfigConstantPicker(selection: $wizardState.systemArchitecture)
|
|
VMConfigConstantPicker(selection: $wizardState.systemArchitecture)
|
|
.onChange(of: wizardState.systemArchitecture) { newValue in
|
|
.onChange(of: wizardState.systemArchitecture) { newValue in
|
|
@@ -135,27 +208,32 @@ struct VMWizardHardwareView: View {
|
|
Text("System")
|
|
Text("System")
|
|
}
|
|
}
|
|
|
|
|
|
- } else if wizardState.operatingSystem == .ClassicMacOS {
|
|
|
|
- Picker("Machine", selection: $classicMacSystem) {
|
|
|
|
- ForEach(ClassicMacSystem.allCases) { system in
|
|
|
|
|
|
+ } else if !isExpertMode {
|
|
|
|
+ Picker("Machine", selection: $selectedMachine) {
|
|
|
|
+ ForEach(SupportedMachine.allCases.filter({ $0.isSupported(running: wizardState.operatingSystem )})) { system in
|
|
Text(system.title).tag(system)
|
|
Text(system.title).tag(system)
|
|
}
|
|
}
|
|
}.pickerStyle(.inline)
|
|
}.pickerStyle(.inline)
|
|
- .onChange(of: classicMacSystem) { newValue in
|
|
|
|
|
|
+ .onChange(of: selectedMachine) { newValue in
|
|
|
|
+ guard let newValue = newValue else {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
wizardState.systemArchitecture = newValue.architecture
|
|
wizardState.systemArchitecture = newValue.architecture
|
|
wizardState.systemTarget = newValue.target
|
|
wizardState.systemTarget = newValue.target
|
|
wizardState.systemMemoryMib = newValue.defaultRam
|
|
wizardState.systemMemoryMib = newValue.defaultRam
|
|
- wizardState.systemCpuCount = 1
|
|
|
|
- wizardState.storageSizeGib = 2
|
|
|
|
|
|
+ wizardState.systemCpuCount = newValue.maxSupportedCores
|
|
|
|
+ wizardState.storageSizeGib = newValue.defaultStorageGiB
|
|
|
|
+ wizardState.legacyHardware = newValue.isLegacyHardware
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Section {
|
|
Section {
|
|
RAMSlider(systemMemory: $wizardState.systemMemoryMib) { _ in
|
|
RAMSlider(systemMemory: $wizardState.systemMemoryMib) { _ in
|
|
- let validMax = wizardState.operatingSystem == .ClassicMacOS ? classicMacSystem.maxRam : maxMemoryMib
|
|
|
|
|
|
+ let selectedMax = selectedMachine?.maxRam ?? 0
|
|
|
|
+ let validMax = selectedMax > 0 ? selectedMax : maxMemoryMib
|
|
if wizardState.systemMemoryMib > validMax {
|
|
if wizardState.systemMemoryMib > validMax {
|
|
wizardState.systemMemoryMib = validMax
|
|
wizardState.systemMemoryMib = validMax
|
|
}
|
|
}
|
|
- let validMin = wizardState.operatingSystem == .ClassicMacOS ? classicMacSystem.minRam : 0
|
|
|
|
|
|
+ let validMin = selectedMachine?.minRam ?? 0
|
|
if wizardState.systemMemoryMib < validMin {
|
|
if wizardState.systemMemoryMib < validMin {
|
|
wizardState.systemMemoryMib = validMin
|
|
wizardState.systemMemoryMib = validMin
|
|
}
|
|
}
|
|
@@ -164,7 +242,7 @@ struct VMWizardHardwareView: View {
|
|
Text("Memory")
|
|
Text("Memory")
|
|
}
|
|
}
|
|
|
|
|
|
- if wizardState.operatingSystem != .ClassicMacOS {
|
|
|
|
|
|
+ if isExpertMode || selectedMachine?.maxSupportedCores == 0 {
|
|
Section {
|
|
Section {
|
|
HStack {
|
|
HStack {
|
|
Stepper(value: $wizardState.systemCpuCount, in: minCores...maxCores) {
|
|
Stepper(value: $wizardState.systemCpuCount, in: minCores...maxCores) {
|
|
@@ -202,10 +280,21 @@ struct VMWizardHardwareView: View {
|
|
.disabled(!wizardState.isDisplayEnabled)
|
|
.disabled(!wizardState.isDisplayEnabled)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if !wizardState.useVirtualization && isExpertMode {
|
|
|
|
+ Section {
|
|
|
|
+ Toggle("Legacy Hardware", isOn: $wizardState.legacyHardware)
|
|
|
|
+ .help("If checked, emulated devices with higher compatibility will be instantiated at the cost of performance.")
|
|
|
|
+ } header: {
|
|
|
|
+ Text("Options")
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
.textFieldStyle(.roundedBorder)
|
|
.textFieldStyle(.roundedBorder)
|
|
.onAppear {
|
|
.onAppear {
|
|
if wizardState.useVirtualization {
|
|
if wizardState.useVirtualization {
|
|
|
|
+ isExpertMode = true
|
|
|
|
+ selectedMachine = nil
|
|
#if arch(arm64)
|
|
#if arch(arm64)
|
|
wizardState.systemArchitecture = .aarch64
|
|
wizardState.systemArchitecture = .aarch64
|
|
#elseif arch(x86_64)
|
|
#elseif arch(x86_64)
|
|
@@ -214,16 +303,15 @@ struct VMWizardHardwareView: View {
|
|
#error("Unsupported architecture.")
|
|
#error("Unsupported architecture.")
|
|
#endif
|
|
#endif
|
|
wizardState.systemTarget = wizardState.systemArchitecture.targetType.default
|
|
wizardState.systemTarget = wizardState.systemArchitecture.targetType.default
|
|
- }
|
|
|
|
- if wizardState.legacyHardware && wizardState.systemArchitecture == .x86_64 {
|
|
|
|
- wizardState.systemTarget = QEMUTarget_x86_64.pc
|
|
|
|
- }
|
|
|
|
- if wizardState.operatingSystem == .ClassicMacOS {
|
|
|
|
- wizardState.systemArchitecture = classicMacSystem.architecture
|
|
|
|
- wizardState.systemTarget = classicMacSystem.target
|
|
|
|
- wizardState.systemMemoryMib = classicMacSystem.defaultRam
|
|
|
|
- wizardState.systemCpuCount = 1
|
|
|
|
- wizardState.storageSizeGib = 2
|
|
|
|
|
|
+ wizardState.legacyHardware = false
|
|
|
|
+ } else if selectedMachine == nil {
|
|
|
|
+ selectedMachine = SupportedMachine.default(for: wizardState.operatingSystem)
|
|
|
|
+ wizardState.systemArchitecture = selectedMachine!.architecture
|
|
|
|
+ wizardState.systemTarget = selectedMachine!.target
|
|
|
|
+ wizardState.systemMemoryMib = selectedMachine!.defaultRam
|
|
|
|
+ wizardState.systemCpuCount = selectedMachine!.maxSupportedCores
|
|
|
|
+ wizardState.storageSizeGib = selectedMachine!.defaultStorageGiB
|
|
|
|
+ wizardState.legacyHardware = selectedMachine!.isLegacyHardware
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|