VMConfigAppleDisplayView.swift 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. //
  2. // Copyright © 2021 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 SwiftUI
  17. @available(macOS 12, *)
  18. struct VMConfigAppleDisplayView: View {
  19. typealias Resolution = UTMAppleConfigurationDisplay
  20. private struct NamedResolution: Identifiable, Hashable {
  21. let name: String
  22. let resolution: Resolution
  23. var id: String {
  24. name
  25. }
  26. func hash(into hasher: inout Hasher) {
  27. resolution.widthInPixels.hash(into: &hasher)
  28. resolution.heightInPixels.hash(into: &hasher)
  29. resolution.pixelsPerInch.hash(into: &hasher)
  30. }
  31. static func == (lhs: NamedResolution, rhs: NamedResolution) -> Bool {
  32. lhs.hashValue == rhs.hashValue
  33. }
  34. static func == (lhs: NamedResolution, rhs: Resolution) -> Bool {
  35. lhs.resolution.widthInPixels == rhs.widthInPixels &&
  36. lhs.resolution.heightInPixels == rhs.heightInPixels
  37. }
  38. }
  39. private static let customResolution = NamedResolution(name: NSLocalizedString("Custom", comment: "VMConfigAppleDisplayView"),
  40. resolution: Resolution(width: 0, height: 0))
  41. private var customResolution: NamedResolution {
  42. Self.customResolution
  43. }
  44. private let resolutions = [
  45. Self.customResolution,
  46. NamedResolution(name: "1024 × 640 — 16:10",
  47. resolution: Resolution(width: 1024, height: 640)),
  48. NamedResolution(name: "1024 × 665 — MacBook Pro (14-inch, 2021) Scaled",
  49. resolution: Resolution(width: 1024, height: 665)),
  50. NamedResolution(name: "1024 × 768 — 4:3 XGA",
  51. resolution: Resolution(width: 1024, height: 768)),
  52. NamedResolution(name: "1147 × 745 — MacBook Pro (14-inch, 2021) Scaled",
  53. resolution: Resolution(width: 1147, height: 745)),
  54. NamedResolution(name: "1152 × 720 — MacBook Pro (16-inch, 2019) Scaled",
  55. resolution: Resolution(width: 1152, height: 720)),
  56. NamedResolution(name: "1168 × 755 — MacBook Pro (16-inch, 2021) Scaled",
  57. resolution: Resolution(width: 1168, height: 755)),
  58. NamedResolution(name: "1280 × 800 — 16:10 WXGA",
  59. resolution: Resolution(width: 1280, height: 800)),
  60. NamedResolution(name: "1280 × 1024 — 5:4 SXGA",
  61. resolution: Resolution(width: 1280, height: 1024)),
  62. NamedResolution(name: "1312 × 848 — MacBook Pro (16-inch, 2021) Scaled",
  63. resolution: Resolution(width: 1312, height: 848)),
  64. NamedResolution(name: "1344 × 840 — MacBook Pro (16-inch, 2019) Scaled",
  65. resolution: Resolution(width: 1344, height: 840)),
  66. NamedResolution(name: "1352 × 878 — MacBook Pro (14-inch, 2021) Scaled",
  67. resolution: Resolution(width: 1352, height: 878)),
  68. NamedResolution(name: "1440 × 900 — 16:10 WXGA+",
  69. resolution: Resolution(width: 1440, height: 900)),
  70. NamedResolution(name: "1496 × 967 — MacBook Pro (16-inch, 2021) Scaled",
  71. resolution: Resolution(width: 1496, height: 967)),
  72. NamedResolution(name: "1512 × 982 — MacBook Pro (14-inch, 2021) Scaled",
  73. resolution: Resolution(width: 1512, height: 982)),
  74. NamedResolution(name: "1680 × 1050 — 16:10 WSXGA+",
  75. resolution: Resolution(width: 1680, height: 1050)),
  76. NamedResolution(name: "1728 × 1117 — MacBook Pro (16-inch, 2021) Scaled",
  77. resolution: Resolution(width: 1728, height: 1117)),
  78. NamedResolution(name: "1792 × 1120 — MacBook Pro (16-inch, 2019) Scaled",
  79. resolution: Resolution(width: 1792, height: 1120)),
  80. NamedResolution(name: "1800 × 1169 — MacBook Pro (14-inch, 2021) Scaled",
  81. resolution: Resolution(width: 1800, height: 1169)),
  82. NamedResolution(name: "1920 × 1080 — Full HD",
  83. resolution: Resolution(width: 1920, height: 1080)),
  84. NamedResolution(name: "1920 × 1200 — 16:10 WUXGA",
  85. resolution: Resolution(width: 1920, height: 1200)),
  86. NamedResolution(name: "2048 × 1280 — MacBook Pro (16-inch, 2019) Scaled",
  87. resolution: Resolution(width: 2048, height: 1280)),
  88. NamedResolution(name: "2056 × 1329 — MacBook Pro (14-inch, 2021) Scaled",
  89. resolution: Resolution(width: 2056, height: 1329)),
  90. NamedResolution(name: "2240 × 1260 — 16:9 Scaled",
  91. resolution: Resolution(width: 2240, height: 1260)),
  92. NamedResolution(name: "2304 × 1440 — MacBook (12-inch, 2015)",
  93. resolution: Resolution(width: 2304, height: 1440)), // ppi: 226
  94. NamedResolution(name: "2560 × 1440 — Quad HD",
  95. resolution: Resolution(width: 2560, height: 1440)),
  96. NamedResolution(name: "2560 × 1600 — MacBook Pro/Air (13-inch, 2012/2018)",
  97. resolution: Resolution(width: 2560, height: 1600)), // ppi: 227
  98. NamedResolution(name: "2560 × 1664 — MacBook Air (13-inch, 2022)",
  99. resolution: Resolution(width: 2560, height: 1664)), // ppi: 224
  100. NamedResolution(name: "2880 × 1800 — MacBook Pro (15-inch, 2012)",
  101. resolution: Resolution(width: 2880, height: 1800)), // ppi: 220
  102. NamedResolution(name: "2880 × 1864 — MacBook Air (15-inch, 2022)",
  103. resolution: Resolution(width: 2880, height: 1864)), // ppi: 224
  104. NamedResolution(name: "3024 × 1890 — MacBook Pro (14-inch, 2021) Full Screen Window",
  105. resolution: Resolution(width: 3024, height: 1890)), // ppi: 254
  106. NamedResolution(name: "3024 × 1964 — MacBook Pro (14-inch, 2021)",
  107. resolution: Resolution(width: 3024, height: 1964)), // ppi: 254
  108. NamedResolution(name: "3072 × 1920 — MacBook Pro (16-inch, 2019)",
  109. resolution: Resolution(width: 3072, height: 1920)), // ppi: 226
  110. NamedResolution(name: "3440 × 1440 — 21:9 Widescreen",
  111. resolution: Resolution(width: 3440, height: 1440)),
  112. NamedResolution(name: "3456 × 2160 — MacBook Pro (16-inch, 2021) Full Screen Window",
  113. resolution: Resolution(width: 3456, height: 2160)), // ppi: 254
  114. NamedResolution(name: "3456 × 2234 — MacBook Pro (16-inch, 2021)",
  115. resolution: Resolution(width: 3456, height: 2234)), // ppi: 254
  116. NamedResolution(name: "3840 × 2160 — 4K Ultra HD",
  117. resolution: Resolution(width: 3840, height: 2160)),
  118. NamedResolution(name: "4480 × 2520 — iMac (24-inch, 2021)",
  119. resolution: Resolution(width: 4480, height: 2520)), // ppi: 218
  120. NamedResolution(name: "5120 × 1440 — 5K Ultra Wide HD",
  121. resolution: Resolution(width: 5120, height: 1440)),
  122. NamedResolution(name: "5120 × 2880 — 5K Ultra HD",
  123. resolution: Resolution(width: 5120, height: 2880)),
  124. ]
  125. @Binding var config: UTMAppleConfigurationDisplay
  126. private var displayResolution: Binding<NamedResolution> {
  127. Binding<NamedResolution> {
  128. for item in resolutions {
  129. if item == config {
  130. return item
  131. }
  132. }
  133. return customResolution
  134. } set: { newValue in
  135. config.widthInPixels = newValue.resolution.widthInPixels
  136. config.heightInPixels = newValue.resolution.heightInPixels
  137. config.pixelsPerInch = newValue.resolution.pixelsPerInch
  138. }
  139. }
  140. private var isHidpi: Binding<Bool> {
  141. Binding<Bool> {
  142. return config.pixelsPerInch >= 226
  143. } set: { newValue in
  144. config.pixelsPerInch = newValue ? 226 : 80
  145. }
  146. }
  147. var body: some View {
  148. Form {
  149. Picker("Resolution", selection: displayResolution) {
  150. ForEach(resolutions) { item in
  151. Text(item.name)
  152. .tag(item)
  153. }
  154. }
  155. if displayResolution.wrappedValue == customResolution {
  156. NumberTextField("Width", number: $config.widthInPixels)
  157. NumberTextField("Height", number: $config.heightInPixels)
  158. }
  159. Toggle("HiDPI (Retina)", isOn: isHidpi)
  160. .help("Only available on macOS virtual machines.")
  161. if #available(macOS 14, *) {
  162. Toggle("Dynamic Resolution", isOn: $config.isDynamicResolution)
  163. .help("Only available on macOS 14+ virtual machines.")
  164. }
  165. }
  166. }
  167. }
  168. @available(macOS 12, *)
  169. struct VMConfigAppleDisplayView_Previews: PreviewProvider {
  170. @State static private var config = UTMAppleConfigurationDisplay()
  171. static var previews: some View {
  172. VMConfigAppleDisplayView(config: $config)
  173. }
  174. }