123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- //
- // Copyright © 2022 osy. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- //
- import Foundation
- @MainActor
- @objc(UTMScriptingVirtualMachineImpl)
- class UTMScriptingVirtualMachineImpl: NSObject {
- private var vm: UTMVirtualMachine
- private var data: UTMData
-
- @objc var id: String {
- vm.id.uuidString
- }
-
- @objc var name: String {
- vm.detailsTitleLabel
- }
-
- @objc var notes: String {
- vm.detailsNotes ?? ""
- }
-
- @objc var machine: String {
- vm.detailsSystemTargetLabel
- }
-
- @objc var architecture: String {
- vm.detailsSystemArchitectureLabel
- }
-
- @objc var memory: String {
- vm.detailsSystemMemoryLabel
- }
-
- @objc var backend: UTMScriptingBackend {
- if vm is UTMQemuVirtualMachine {
- return .qemu
- } else if vm is UTMAppleVirtualMachine {
- return .apple
- } else {
- return .unavailable
- }
- }
-
- @objc var status: UTMScriptingStatus {
- switch vm.state {
- case .vmStopped: return .stopped
- case .vmStarting: return .starting
- case .vmStarted: return .started
- case .vmPausing: return .pausing
- case .vmPaused: return .paused
- case .vmResuming: return .resuming
- case .vmStopping: return .stopping
- @unknown default: return .stopped
- }
- }
-
- @objc var serialPorts: [UTMScriptingSerialPortImpl] {
- if let config = vm.config.qemuConfig {
- return config.serials.indices.map({ UTMScriptingSerialPortImpl(qemuSerial: config.serials[$0], parent: self, index: $0) })
- } else if let config = vm.config.appleConfig {
- return config.serials.indices.map({ UTMScriptingSerialPortImpl(appleSerial: config.serials[$0], parent: self, index: $0) })
- } else {
- return []
- }
- }
-
- override var objectSpecifier: NSScriptObjectSpecifier? {
- let appDescription = NSApplication.classDescription() as! NSScriptClassDescription
- return NSUniqueIDSpecifier(containerClassDescription: appDescription,
- containerSpecifier: nil,
- key: "scriptingVirtualMachines",
- uniqueID: id)
- }
-
- init(for vm: UTMVirtualMachine, data: UTMData) {
- self.vm = vm
- self.data = data
- }
-
- private func withScriptCommand<Result>(_ command: NSScriptCommand, body: @MainActor @escaping () async throws -> Result) {
- guard command.evaluatedReceivers as? Self == self else {
- return
- }
- command.suspendExecution()
- // we need to run this in next event loop due to the need to return before calling resume
- DispatchQueue.main.async {
- Task {
- do {
- let result = try await body()
- await MainActor.run {
- if result is Void {
- command.resumeExecution(withResult: nil)
- } else {
- command.resumeExecution(withResult: result)
- }
- }
- } catch {
- await MainActor.run {
- command.scriptErrorNumber = errOSAGeneralError
- command.scriptErrorString = error.localizedDescription
- command.resumeExecution(withResult: nil)
- }
- }
- }
- }
- }
-
- @objc func start(_ command: NSScriptCommand) {
- let shouldSaveState = command.evaluatedArguments?["saveFlag"] as? Bool ?? true
- withScriptCommand(command) { [self] in
- if !shouldSaveState {
- guard let vm = vm as? UTMQemuVirtualMachine else {
- throw ScriptingError.operationNotSupported
- }
- vm.isRunningAsSnapshot = true
- }
- data.run(vm: vm, startImmediately: false)
- if vm.state == .vmStopped {
- try await vm.vmStart()
- } else if vm.state == .vmPaused {
- try await vm.vmResume()
- } else {
- throw ScriptingError.operationNotAvailable
- }
- }
- }
-
- @objc func suspend(_ command: NSScriptCommand) {
- let shouldSaveState = command.evaluatedArguments?["saveFlag"] as? Bool ?? false
- withScriptCommand(command) { [self] in
- try await vm.vmPause(save: shouldSaveState)
- }
- }
-
- @objc func stop(_ command: NSScriptCommand) {
- let stopMethod = command.evaluatedArguments?["stopBy"] as? UTMScriptingStopMethod ?? .force
- withScriptCommand(command) { [self] in
- switch stopMethod {
- case .force:
- try await vm.vmStop(force: false)
- case .kill:
- try await vm.vmStop(force: true)
- case .request:
- vm.requestGuestPowerDown()
- }
- }
- }
- }
- extension UTMScriptingVirtualMachineImpl {
- enum ScriptingError: Error, LocalizedError {
- case operationNotAvailable
- case operationNotSupported
-
- var localizedDescription: String {
- switch self {
- case .operationNotAvailable: return NSLocalizedString("Operation not available.", comment: "UTMScriptingVirtualMachineImpl")
- case .operationNotSupported: return NSLocalizedString("Operation not supported by the backend.", comment: "UTMScriptingVirtualMachineImpl")
- }
- }
- }
- }
|