UTMUSBManager.swift 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. //
  2. // Copyright © 2025 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 CocoaSpice
  18. final class UTMUSBManager {
  19. struct USBDevice: Codable, Hashable {
  20. var usbVendorId: Int
  21. var usbProductId: Int
  22. var usbManufacturerName: String?
  23. var usbProductName: String?
  24. var usbSerial: String?
  25. fileprivate init(_ device: CSUSBDevice) {
  26. usbVendorId = device.usbVendorId
  27. usbProductId = device.usbProductId
  28. usbManufacturerName = device.usbManufacturerName
  29. usbProductName = device.usbProductName
  30. usbSerial = device.usbSerial
  31. }
  32. }
  33. static let shared = UTMUSBManager()
  34. @Setting("SavedUsbDevices") private var savedUsbDevices: Data? = nil
  35. lazy var usbDevices: [USBDevice: UUID] = loadUsbDevices() {
  36. didSet {
  37. saveUsbDevices(usbDevices)
  38. }
  39. }
  40. private init() {}
  41. private func loadUsbDevices() -> [USBDevice: UUID] {
  42. let decoder = PropertyListDecoder()
  43. if let data = savedUsbDevices {
  44. if let decoded = try? decoder.decode([USBDevice: UUID].self, from: data) {
  45. return decoded
  46. }
  47. }
  48. // default entry
  49. return [:]
  50. }
  51. private func saveUsbDevices(_ usbDevices: [USBDevice: UUID]) {
  52. let encoder = PropertyListEncoder()
  53. encoder.outputFormat = .binary
  54. if let data = try? encoder.encode(usbDevices) {
  55. savedUsbDevices = data
  56. }
  57. }
  58. }
  59. extension UTMVirtualMachine {
  60. func isAutoConnect(for device: CSUSBDevice) -> Bool {
  61. let usbDevice = UTMUSBManager.USBDevice(device)
  62. return UTMUSBManager.shared.usbDevices[usbDevice] == self.id
  63. }
  64. func setAutoConnect(_ autoConnect: Bool, for device: CSUSBDevice) {
  65. let usbDevice = UTMUSBManager.USBDevice(device)
  66. if autoConnect {
  67. UTMUSBManager.shared.usbDevices[usbDevice] = self.id
  68. } else {
  69. UTMUSBManager.shared.usbDevices.removeValue(forKey: usbDevice)
  70. }
  71. }
  72. }