QEMUConstant.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. //
  2. // Copyright © 2022 osy. All rights reserved.
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. import Foundation
  17. import Metal
  18. // MARK: QEMUConstant protocol
  19. /// A QEMU constant is a enum that can be generated externally
  20. protocol QEMUConstant: Codable, RawRepresentable, CaseIterable where RawValue == String, AllCases == [Self] {
  21. static var allRawValues: [String] { get }
  22. static var allPrettyValues: [String] { get }
  23. var prettyValue: String { get }
  24. var rawValue: String { get }
  25. init?(rawValue: String)
  26. }
  27. extension QEMUConstant where Self: CaseIterable, AllCases == [Self] {
  28. static var allRawValues: [String] {
  29. allCases.map { value in value.rawValue }
  30. }
  31. static var allPrettyValues: [String] {
  32. allCases.map { value in value.prettyValue }
  33. }
  34. }
  35. extension QEMUConstant where Self: RawRepresentable, RawValue == String {
  36. init(from decoder: Decoder) throws {
  37. let rawValue = try String(from: decoder)
  38. guard let representedValue = Self.init(rawValue: rawValue) else {
  39. throw UTMConfigurationError.invalidConfigurationValue(rawValue)
  40. }
  41. self = representedValue
  42. }
  43. func encode(to encoder: Encoder) throws {
  44. try rawValue.encode(to: encoder)
  45. }
  46. }
  47. protocol QEMUDefaultConstant: QEMUConstant {
  48. static var `default`: Self { get }
  49. }
  50. extension Optional where Wrapped: QEMUDefaultConstant {
  51. var _bound: Wrapped? {
  52. get {
  53. return self
  54. }
  55. set {
  56. self = newValue
  57. }
  58. }
  59. var bound: Wrapped {
  60. get {
  61. return _bound ?? Wrapped.default
  62. }
  63. set {
  64. _bound = newValue
  65. }
  66. }
  67. }
  68. /// Type erasure for a QEMU constant useful for serialization/deserialization
  69. struct AnyQEMUConstant: QEMUConstant, RawRepresentable {
  70. static var allRawValues: [String] { [] }
  71. static var allPrettyValues: [String] { [] }
  72. static var allCases: [AnyQEMUConstant] { [] }
  73. var prettyValue: String { rawValue }
  74. let rawValue: String
  75. init<C>(_ base: C) where C : QEMUConstant {
  76. self.rawValue = base.rawValue
  77. }
  78. init?(rawValue: String) {
  79. self.rawValue = rawValue
  80. }
  81. }
  82. extension QEMUConstant {
  83. func asAnyQEMUConstant() -> AnyQEMUConstant {
  84. return AnyQEMUConstant(self)
  85. }
  86. }
  87. extension AnyQEMUConstant: QEMUDefaultConstant {
  88. static var `default`: AnyQEMUConstant {
  89. AnyQEMUConstant(rawValue: "default")!
  90. }
  91. }
  92. // MARK: Enhanced type checking for generated constants
  93. protocol QEMUTarget: QEMUDefaultConstant {}
  94. extension AnyQEMUConstant: QEMUTarget {}
  95. protocol QEMUCPU: QEMUDefaultConstant {}
  96. extension AnyQEMUConstant: QEMUCPU {}
  97. protocol QEMUCPUFlag: QEMUConstant {}
  98. extension AnyQEMUConstant: QEMUCPUFlag {}
  99. protocol QEMUDisplayDevice: QEMUConstant {}
  100. extension AnyQEMUConstant: QEMUDisplayDevice {}
  101. protocol QEMUNetworkDevice: QEMUConstant {}
  102. extension AnyQEMUConstant: QEMUNetworkDevice {}
  103. protocol QEMUSoundDevice: QEMUConstant {}
  104. extension AnyQEMUConstant: QEMUSoundDevice {}
  105. protocol QEMUSerialDevice: QEMUConstant {}
  106. extension AnyQEMUConstant: QEMUSerialDevice {}
  107. // MARK: Display constants
  108. enum QEMUScaler: String, CaseIterable, QEMUConstant {
  109. case linear = "Linear"
  110. case nearest = "Nearest"
  111. var prettyValue: String {
  112. switch self {
  113. case .linear: return NSLocalizedString("Linear", comment: "UTMQemuConstants")
  114. case .nearest: return NSLocalizedString("Nearest Neighbor", comment: "UTMQemuConstants")
  115. }
  116. }
  117. var metalSamplerMinMagFilter: MTLSamplerMinMagFilter {
  118. switch self {
  119. case .linear: return .linear
  120. case .nearest: return .nearest
  121. }
  122. }
  123. }
  124. // MARK: USB constants
  125. enum QEMUUSBBus: String, CaseIterable, QEMUConstant {
  126. case disabled = "Disabled"
  127. case usb2_0 = "2.0"
  128. case usb3_0 = "3.0"
  129. var prettyValue: String {
  130. switch self {
  131. case .disabled: return NSLocalizedString("Disabled", comment: "UTMQemuConstants")
  132. case .usb2_0: return NSLocalizedString("USB 2.0", comment: "UTMQemuConstants")
  133. case .usb3_0: return NSLocalizedString("USB 3.0 (XHCI)", comment: "UTMQemuConstants")
  134. }
  135. }
  136. }
  137. // MARK: Network constants
  138. enum QEMUNetworkMode: String, CaseIterable, QEMUConstant {
  139. case emulated = "Emulated"
  140. case shared = "Shared"
  141. case host = "Host"
  142. case bridged = "Bridged"
  143. var prettyValue: String {
  144. switch self {
  145. case .emulated: return NSLocalizedString("Emulated VLAN", comment: "UTMQemuConstants")
  146. case .shared: return NSLocalizedString("Shared Network", comment: "UTMQemuConstants")
  147. case .host: return NSLocalizedString("Host Only", comment: "UTMQemuConstants")
  148. case .bridged: return NSLocalizedString("Bridged (Advanced)", comment: "UTMQemuConstants")
  149. }
  150. }
  151. }
  152. enum QEMUNetworkProtocol: String, CaseIterable, QEMUConstant {
  153. case tcp = "TCP"
  154. case udp = "UDP"
  155. var prettyValue: String {
  156. switch self {
  157. case .tcp: return NSLocalizedString("TCP", comment: "UTMQemuConstants")
  158. case .udp: return NSLocalizedString("UDP", comment: "UTMQemuConstants")
  159. }
  160. }
  161. }
  162. // MARK: Serial constants
  163. enum QEMUTerminalTheme: String, CaseIterable, QEMUDefaultConstant {
  164. case `default` = "Default"
  165. var prettyValue: String {
  166. switch self {
  167. case .`default`: return NSLocalizedString("Default", comment: "UTMQemuConstants")
  168. }
  169. }
  170. }
  171. struct QEMUTerminalFont: QEMUConstant {
  172. #if os(macOS)
  173. static var allRawValues: [String] = {
  174. NSFontManager.shared.availableFontNames(with: .fixedPitchFontMask) ?? []
  175. }()
  176. static var allPrettyValues: [String] = {
  177. allRawValues.map { name in
  178. NSFont(name: name, size: 1)?.displayName ?? name
  179. }
  180. }()
  181. #else
  182. static var allRawValues: [String] = {
  183. UIFont.familyNames.flatMap { family -> [String] in
  184. guard let font = UIFont(name: family, size: 1) else {
  185. return []
  186. }
  187. if font.fontDescriptor.symbolicTraits.contains(.traitMonoSpace) {
  188. return UIFont.fontNames(forFamilyName: family)
  189. } else {
  190. return []
  191. }
  192. }
  193. }()
  194. static var allPrettyValues: [String] = {
  195. allRawValues.map { name in
  196. guard let font = UIFont(name: name, size: 1) else {
  197. return name
  198. }
  199. let traits = font.fontDescriptor.symbolicTraits
  200. let description: String
  201. if traits.isSuperset(of: [.traitItalic, .traitBold]) {
  202. description = NSLocalizedString("Italic, Bold", comment: "UTMQemuConstants")
  203. } else if traits.contains(.traitItalic) {
  204. description = NSLocalizedString("Italic", comment: "UTMQemuConstants")
  205. } else if traits.contains(.traitBold) {
  206. description = NSLocalizedString("Bold", comment: "UTMQemuConstants")
  207. } else {
  208. description = NSLocalizedString("Regular", comment: "UTMQemuConstants")
  209. }
  210. return String.localizedStringWithFormat(NSLocalizedString("%@ (%@)", comment: "QEMUConstant"), font.familyName, description)
  211. }
  212. }()
  213. #endif
  214. static var allCases: [QEMUTerminalFont] {
  215. Self.allRawValues.map { Self(rawValue: $0) }
  216. }
  217. var prettyValue: String {
  218. guard let index = Self.allRawValues.firstIndex(of: rawValue) else {
  219. return rawValue
  220. }
  221. return Self.allPrettyValues[index]
  222. }
  223. let rawValue: String
  224. }
  225. enum QEMUSerialMode: String, CaseIterable, QEMUConstant {
  226. case builtin = "Terminal"
  227. case tcpClient = "TcpClient"
  228. case tcpServer = "TcpServer"
  229. #if os(macOS)
  230. case ptty = "Ptty"
  231. #endif
  232. var prettyValue: String {
  233. switch self {
  234. case .builtin: return NSLocalizedString("Built-in Terminal", comment: "UTMQemuConstants")
  235. case .tcpClient: return NSLocalizedString("TCP Client Connection", comment: "UTMQemuConstants")
  236. case .tcpServer: return NSLocalizedString("TCP Server Connection", comment: "UTMQemuConstants")
  237. #if os(macOS)
  238. case .ptty: return NSLocalizedString("Pseudo-TTY Device", comment: "UTMQemuConstants")
  239. #endif
  240. }
  241. }
  242. }
  243. enum QEMUSerialTarget: String, CaseIterable, QEMUConstant {
  244. case autoDevice = "Auto"
  245. case manualDevice = "Manual"
  246. case gdb = "GDB"
  247. case monitor = "Monitor"
  248. var prettyValue: String {
  249. switch self {
  250. case .autoDevice: return NSLocalizedString("Automatic Serial Device (max 4)", comment: "UTMQemuConstants")
  251. case .manualDevice: return NSLocalizedString("Manual Serial Device (advanced)", comment: "UTMQemuConstants")
  252. case .gdb: return NSLocalizedString("GDB Debug Stub", comment: "UTMQemuConstants")
  253. case .monitor: return NSLocalizedString("QEMU Monitor (HMP)", comment: "UTMQemuConstants")
  254. }
  255. }
  256. }
  257. // MARK: Drive constants
  258. enum QEMUDriveImageType: String, CaseIterable, QEMUConstant {
  259. case none = "None"
  260. case disk = "Disk"
  261. case cd = "CD"
  262. case bios = "BIOS"
  263. case linuxKernel = "LinuxKernel"
  264. case linuxInitrd = "LinuxInitrd"
  265. case linuxDtb = "LinuxDTB"
  266. var prettyValue: String {
  267. switch self {
  268. case .none: return NSLocalizedString("None", comment: "UTMQemuConstants")
  269. case .disk: return NSLocalizedString("Disk Image", comment: "UTMQemuConstants")
  270. case .cd: return NSLocalizedString("CD/DVD (ISO) Image", comment: "UTMQemuConstants")
  271. case .bios: return NSLocalizedString("BIOS", comment: "UTMQemuConstants")
  272. case .linuxKernel: return NSLocalizedString("Linux Kernel", comment: "UTMQemuConstants")
  273. case .linuxInitrd: return NSLocalizedString("Linux RAM Disk", comment: "UTMQemuConstants")
  274. case .linuxDtb: return NSLocalizedString("Linux Device Tree Binary", comment: "UTMQemuConstants")
  275. }
  276. }
  277. }
  278. enum QEMUDriveInterface: String, CaseIterable, QEMUConstant {
  279. case none = "None"
  280. case ide = "IDE"
  281. case scsi = "SCSI"
  282. case sd = "SD"
  283. case mtd = "MTD"
  284. case floppy = "Floppy"
  285. case pflash = "PFlash"
  286. case virtio = "VirtIO"
  287. case nvme = "NVMe"
  288. case usb = "USB"
  289. var prettyValue: String {
  290. switch self {
  291. case .none: return NSLocalizedString("None (Advanced)", comment: "UTMQemuConstants")
  292. case .ide: return NSLocalizedString("IDE", comment: "UTMQemuConstants")
  293. case .scsi: return NSLocalizedString("SCSI", comment: "UTMQemuConstants")
  294. case .sd: return NSLocalizedString("SD Card", comment: "UTMQemuConstants")
  295. case .mtd: return NSLocalizedString("MTD (NAND/NOR)", comment: "UTMQemuConstants")
  296. case .floppy: return NSLocalizedString("Floppy", comment: "UTMQemuConstants")
  297. case .pflash: return NSLocalizedString("PC System Flash", comment: "UTMQemuConstants")
  298. case .virtio: return NSLocalizedString("VirtIO", comment: "UTMQemuConstants")
  299. case .nvme: return NSLocalizedString("NVMe", comment: "UTMQemuConstants")
  300. case .usb: return NSLocalizedString("USB", comment: "UTMQemuConstants")
  301. }
  302. }
  303. }
  304. // MARK: Sharing constants
  305. enum QEMUFileShareMode: String, CaseIterable, QEMUConstant {
  306. case none = "None"
  307. case webdav = "WebDAV"
  308. case virtfs = "VirtFS"
  309. var prettyValue: String {
  310. switch self {
  311. case .none: return NSLocalizedString("None", comment: "UTMQemuConstants")
  312. case .webdav: return NSLocalizedString("SPICE WebDAV", comment: "UTMQemuConstants")
  313. case .virtfs: return NSLocalizedString("VirtFS", comment: "UTMQemuConstants")
  314. }
  315. }
  316. }
  317. // MARK: File names
  318. enum QEMUPackageFileName: String {
  319. case images = "Images"
  320. case debugLog = "debug.log"
  321. case efiVariables = "efi_vars.fd"
  322. }
  323. // MARK: Supported features
  324. extension QEMUArchitecture {
  325. var hasAgentSupport: Bool {
  326. switch self {
  327. case .sparc, .sparc64: return false
  328. default: return true
  329. }
  330. }
  331. var hasSharingSupport: Bool {
  332. switch self {
  333. case .sparc, .sparc64: return false
  334. default: return true
  335. }
  336. }
  337. var hasUsbSupport: Bool {
  338. switch self {
  339. case .s390x: return false
  340. case .sparc, .sparc64: return false
  341. default: return true
  342. }
  343. }
  344. var hasHypervisorSupport: Bool {
  345. guard jb_has_hypervisor() else {
  346. return false
  347. }
  348. #if arch(arm64)
  349. return self == .aarch64
  350. #elseif arch(x86_64)
  351. return self == .x86_64
  352. #else
  353. return false
  354. #endif
  355. }
  356. }
  357. extension QEMUTarget {
  358. var hasUsbSupport: Bool {
  359. switch self.rawValue {
  360. case "isapc": return false
  361. default: return true
  362. }
  363. }
  364. }