2
0

UTMActionIntent.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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 AppIntents
  17. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  18. struct UTMStatusActionIntent: UTMIntent {
  19. static let title: LocalizedStringResource = "Get Virtual Machine Status"
  20. static let description = IntentDescription("Get the status of a virtual machine.")
  21. static var parameterSummary: some ParameterSummary {
  22. Summary("Get \(\.$vmEntity) status")
  23. }
  24. @Dependency
  25. var data: UTMData
  26. @Parameter(title: "Virtual Machine", requestValueDialog: "Select a virtual machine")
  27. var vmEntity: UTMVirtualMachineEntity
  28. @MainActor
  29. func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some ReturnsValue<UTMVirtualMachineState> {
  30. return .result(value: vm.state)
  31. }
  32. }
  33. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  34. struct UTMStartActionIntent: UTMIntent {
  35. static let title: LocalizedStringResource = "Start Virtual Machine"
  36. static let description = IntentDescription("Start a virtual machine.")
  37. static var parameterSummary: some ParameterSummary {
  38. Summary("Start \(\.$vmEntity)") {
  39. \.$isRecovery
  40. \.$isDisposible
  41. }
  42. }
  43. @Dependency
  44. var data: UTMData
  45. @Parameter(title: "Virtual Machine", requestValueDialog: "Select a virtual machine")
  46. var vmEntity: UTMVirtualMachineEntity
  47. @Parameter(title: "Recovery", description: "Boot into recovery mode. Only supported on Apple Virtualization backend.", default: false)
  48. var isRecovery: Bool
  49. @Parameter(title: "Disposible", description: "Do not save any changes to disk. Only supported on QEMU backend.", default: false)
  50. var isDisposible: Bool
  51. @MainActor
  52. func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
  53. var options = UTMVirtualMachineStartOptions()
  54. if isRecovery {
  55. #if os(macOS)
  56. guard vm is UTMAppleVirtualMachine else {
  57. throw UTMIntentError.unsupportedBackend
  58. }
  59. options.insert(.bootRecovery)
  60. #else
  61. throw UTMIntentError.unsupportedBackend
  62. #endif
  63. }
  64. if isDisposible {
  65. guard vm is UTMQemuVirtualMachine else {
  66. throw UTMIntentError.unsupportedBackend
  67. }
  68. options.insert(.bootDisposibleMode)
  69. }
  70. data.run(vm: boxed, options: options)
  71. if !vm.isHeadless {
  72. if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *), systemContext.currentMode.canContinueInForeground {
  73. try await continueInForeground(alwaysConfirm: false)
  74. }
  75. }
  76. return .result()
  77. }
  78. }
  79. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  80. struct UTMStopActionIntent: UTMIntent {
  81. static let title: LocalizedStringResource = "Stop Virtual Machine"
  82. static let description = IntentDescription("Stop a virtual machine.")
  83. static var parameterSummary: some ParameterSummary {
  84. Summary("Stop \(\.$vmEntity) by \(\.$method)")
  85. }
  86. @Dependency
  87. var data: UTMData
  88. @Parameter(title: "Virtual Machine", requestValueDialog: "Select a virtual machine")
  89. var vmEntity: UTMVirtualMachineEntity
  90. @Parameter(title: "Stop Method", description: "Intensity of the stop action.", default: .force)
  91. var method: UTMVirtualMachineStopMethod
  92. @MainActor
  93. func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
  94. try await vm.stop(usingMethod: method)
  95. return .result()
  96. }
  97. }
  98. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  99. extension UTMVirtualMachineStopMethod: AppEnum {
  100. static let typeDisplayRepresentation: TypeDisplayRepresentation =
  101. TypeDisplayRepresentation(
  102. name: "Stop Method"
  103. )
  104. static let caseDisplayRepresentations: [UTMVirtualMachineStopMethod: DisplayRepresentation] = [
  105. .request: DisplayRepresentation(title: "Request", subtitle: "Sends power down request to the guest. This simulates pressing the power button on a PC."),
  106. .force: DisplayRepresentation(title: "Force", subtitle: "Tells the VM process to shut down with risk of data corruption. This simulates holding down the power button on a PC."),
  107. .kill: DisplayRepresentation(title: "Killing", subtitle: "Force kill the VM process with high risk of data corruption."),
  108. ]
  109. }
  110. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  111. struct UTMPauseActionIntent: UTMIntent {
  112. static let title: LocalizedStringResource = "Pause Virtual Machine"
  113. static let description = IntentDescription("Pause a virtual machine.")
  114. static var parameterSummary: some ParameterSummary {
  115. Summary("Pause \(\.$vmEntity)") {
  116. \.$isSaveState
  117. }
  118. }
  119. @Dependency
  120. var data: UTMData
  121. @Parameter(title: "Virtual Machine", requestValueDialog: "Select a virtual machine")
  122. var vmEntity: UTMVirtualMachineEntity
  123. @Parameter(title: "Save State", description: "Create a snapshot of the virtual machine state.", default: false)
  124. var isSaveState: Bool
  125. @MainActor
  126. func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
  127. try await vm.pause()
  128. if isSaveState {
  129. try await vm.save()
  130. }
  131. return .result()
  132. }
  133. }
  134. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  135. struct UTMResumeActionIntent: UTMIntent {
  136. static let title: LocalizedStringResource = "Resume Virtual Machine"
  137. static let description = IntentDescription("Resume a virtual machine.")
  138. static var parameterSummary: some ParameterSummary {
  139. Summary("Resume \(\.$vmEntity)")
  140. }
  141. @Dependency
  142. var data: UTMData
  143. @Parameter(title: "Virtual Machine", requestValueDialog: "Select a virtual machine")
  144. var vmEntity: UTMVirtualMachineEntity
  145. @MainActor
  146. func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
  147. try await vm.resume()
  148. if vm.isHeadless {
  149. if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *), systemContext.currentMode.canContinueInForeground {
  150. try await continueInForeground(alwaysConfirm: false)
  151. }
  152. }
  153. return .result()
  154. }
  155. }
  156. @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
  157. struct UTMRestartActionIntent: UTMIntent {
  158. static let title: LocalizedStringResource = "Restart Virtual Machine"
  159. static let description = IntentDescription("Restart a virtual machine.")
  160. static var parameterSummary: some ParameterSummary {
  161. Summary("Restart \(\.$vmEntity)")
  162. }
  163. @Dependency
  164. var data: UTMData
  165. @Parameter(title: "Virtual Machine", requestValueDialog: "Select a virtual machine")
  166. var vmEntity: UTMVirtualMachineEntity
  167. @MainActor
  168. func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
  169. try await vm.restart()
  170. if vm.isHeadless {
  171. if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *), systemContext.currentMode.canContinueInForeground {
  172. try await continueInForeground(alwaysConfirm: false)
  173. }
  174. }
  175. return .result()
  176. }
  177. }