@@ -108,6 +108,7 @@ extension UTMScriptingConfigImpl {
108108 " drives " : config. drives. map ( { serializeQemuDriveExisting ( $0) } ) ,
109109 " networkInterfaces " : config. networks. enumerated ( ) . map ( { serializeQemuNetwork ( $1, index: $0) } ) ,
110110 " serialPorts " : config. serials. enumerated ( ) . map ( { serializeQemuSerial ( $1, index: $0) } ) ,
111+ " displays " : config. displays. map ( { serializeQemuDisplay ( $0) } ) ,
111112 " qemuAdditionalArguments " : config. qemu. additionalArguments. map ( { serializeQemuAdditionalArgument ( $0) } ) ,
112113 ]
113114 }
@@ -190,6 +191,24 @@ extension UTMScriptingConfigImpl {
190191 ]
191192 }
192193
194+ private func qemuScaler( from filter: QEMUScaler ) -> UTMScriptingQemuScaler {
195+ switch filter {
196+ case . linear: return . linear
197+ case . nearest: return . nearest
198+ }
199+ }
200+
201+ private func serializeQemuDisplay( _ config: UTMQemuConfigurationDisplay ) -> [ AnyHashable : Any ] {
202+ [
203+ " id " : config. id. uuidString,
204+ " hardware " : config. hardware. rawValue,
205+ " dynamicResolution " : config. isDynamicResolution,
206+ " nativeResolution " : config. isNativeResolution,
207+ " upscalingFilter " : qemuScaler ( from: config. upscalingFilter) . rawValue,
208+ " downscalingFilter " : qemuScaler ( from: config. downscalingFilter) . rawValue,
209+ ]
210+ }
211+
193212 private func serializeQemuAdditionalArgument( _ argument: QEMUArgument ) -> [ AnyHashable : Any ] {
194213 var serializedArgument : [ AnyHashable : Any ] = [
195214 " argumentString " : argument. string
@@ -213,6 +232,7 @@ extension UTMScriptingConfigImpl {
213232 " drives " : config. drives. map ( { serializeAppleDriveExisting ( $0) } ) ,
214233 " networkInterfaces " : config. networks. enumerated ( ) . map ( { serializeAppleNetwork ( $1, index: $0) } ) ,
215234 " serialPorts " : config. serials. enumerated ( ) . map ( { serializeAppleSerial ( $1, index: $0) } ) ,
235+ " displays " : config. displays. map ( { serializeAppleDisplay ( $0) } ) ,
216236 ]
217237 }
218238
@@ -260,6 +280,13 @@ extension UTMScriptingConfigImpl {
260280 " interface " : appleSerialInterface ( from: config. mode) . rawValue,
261281 ]
262282 }
283+
284+ private func serializeAppleDisplay( _ config: UTMAppleConfigurationDisplay ) -> [ AnyHashable : Any ] {
285+ [
286+ " id " : config. id. uuidString,
287+ " dynamicResolution " : config. isDynamicResolution,
288+ ]
289+ }
263290}
264291
265292@MainActor
@@ -360,6 +387,9 @@ extension UTMScriptingConfigImpl {
360387 if let serialPorts = record [ " serialPorts " ] as? [ [ AnyHashable : Any ] ] {
361388 try updateQemuSerials ( from: serialPorts)
362389 }
390+ if let displays = record [ " displays " ] as? [ [ AnyHashable : Any ] ] {
391+ try updateQemuDisplays ( from: displays)
392+ }
363393 if let qemuAdditionalArguments = record [ " qemuAdditionalArguments " ] as? [ [ AnyHashable : Any ] ] {
364394 try updateQemuAdditionalArguments ( from: qemuAdditionalArguments)
365395 }
@@ -525,6 +555,47 @@ extension UTMScriptingConfigImpl {
525555 }
526556 }
527557
558+ private func updateQemuDisplays( from records: [ [ AnyHashable : Any ] ] ) throws {
559+ let config = config as! UTMQemuConfiguration
560+ try updateElements ( & config. displays, with: records, onExisting: updateQemuExistingDisplay, onNew: { record in
561+ guard var newDisplay = UTMQemuConfigurationDisplay ( forArchitecture: config. system. architecture, target: config. system. target) else {
562+ throw ConfigurationError . deviceNotSupported
563+ }
564+ try updateQemuExistingDisplay ( & newDisplay, from: record)
565+ return newDisplay
566+ } )
567+ }
568+
569+ private func parseQemuScaler( _ value: AEKeyword ? ) -> QEMUScaler ? {
570+ guard let value = value, let parsed = UTMScriptingQemuScaler ( rawValue: value) else {
571+ return Optional . none
572+ }
573+ switch parsed {
574+ case . linear: return . linear
575+ case . nearest: return . nearest
576+ default : return Optional . none
577+ }
578+ }
579+
580+ private func updateQemuExistingDisplay( _ display: inout UTMQemuConfigurationDisplay , from record: [ AnyHashable : Any ] ) throws {
581+ let config = config as! UTMQemuConfiguration
582+ if let hardware = record [ " hardware " ] as? String , let hardware = config. system. architecture. displayDeviceType. init ( rawValue: hardware) {
583+ display. hardware = hardware
584+ }
585+ if let dynamicResolution = record [ " dynamicResolution " ] as? Bool {
586+ display. isDynamicResolution = dynamicResolution
587+ }
588+ if let nativeResolution = record [ " nativeResolution " ] as? Bool {
589+ display. isNativeResolution = nativeResolution
590+ }
591+ if let upscalingFilter = parseQemuScaler ( record [ " upscalingFilter " ] as? AEKeyword ) {
592+ display. upscalingFilter = upscalingFilter
593+ }
594+ if let downscalingFilter = parseQemuScaler ( record [ " downscalingFilter " ] as? AEKeyword ) {
595+ display. downscalingFilter = downscalingFilter
596+ }
597+ }
598+
528599 private func updateQemuAdditionalArguments( from records: [ [ AnyHashable : Any ] ] ) throws {
529600 let config = config as! UTMQemuConfiguration
530601 let additionalArguments = records. compactMap { record -> QEMUArgument ? in
@@ -574,6 +645,9 @@ extension UTMScriptingConfigImpl {
574645 if let serialPorts = record [ " serialPorts " ] as? [ [ AnyHashable : Any ] ] {
575646 try updateAppleSerials ( from: serialPorts)
576647 }
648+ if let displays = record [ " displays " ] as? [ [ AnyHashable : Any ] ] {
649+ try updateAppleDisplays ( from: displays)
650+ }
577651 }
578652
579653 private func updateAppleDirectoryShares( from records: [ [ AnyHashable : Any ] ] ) throws {
@@ -670,6 +744,20 @@ extension UTMScriptingConfigImpl {
670744 }
671745 }
672746
747+ private func updateAppleDisplays( from records: [ [ AnyHashable : Any ] ] ) throws {
748+ let config = config as! UTMAppleConfiguration
749+ try updateElements ( & config. displays, with: records, onExisting: updateAppleExistingDisplay, onNew: { record in
750+ var newDisplay = UTMAppleConfigurationDisplay ( )
751+ try updateAppleExistingDisplay ( & newDisplay, from: record)
752+ return newDisplay
753+ } )
754+ }
755+
756+ private func updateAppleExistingDisplay( _ display: inout UTMAppleConfigurationDisplay , from record: [ AnyHashable : Any ] ) throws {
757+ if let dynamicResolution = record [ " dynamicResolution " ] as? Bool {
758+ display. isDynamicResolution = dynamicResolution
759+ }
760+ }
673761 enum ConfigurationError : Error , LocalizedError {
674762 case identifierNotFound( id: any Hashable )
675763 case invalidDriveDescription
0 commit comments