mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
"Run without saving changes" has only been available for QEMU, where it maps to `-snapshot`. Apple Virtualization has no such option, but DiskImageKit on macOS 27 can stack a copy-on-write layer on top of an image and Virtualization accepts the stacked image as a storage attachment. When started with the disposable option, the Apple backend now creates a temporary directory in the sandbox and gives every writable drive an ephemeral ASIF overlay there. The base image is opened read-only and is never touched. The EFI variable store and the macOS auxiliary storage are cloned into the same directory so firmware settings are not persisted either. The directory is deleted when the guest stops or when start fails, any leftover from a previous start is discarded before the next start, and directories orphaned by a crash are swept at app launch. Removable drives attached at start or hot-plugged during the run are covered as well. A disposable run boots fresh and leaves any saved suspend state and the modification date alone, and reports the save/restore path as unsupported so closing the window offers to kill the VM instead of suspending it, matching the QEMU behaviour. The stop paths that delete a saved suspend state skip that while running disposably. The context menu, the Start intent and the menu bar extra now check the backend capability instead of assuming QEMU, and the Apple display window shows the same "Disposable Mode" subtitle as QEMU. Assisted-by: Claude:claude-fable-5-1
81 lines
2.6 KiB
Swift
81 lines
2.6 KiB
Swift
//
|
|
// 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 Combine
|
|
import SwiftUI
|
|
|
|
@available(macOS 13, *)
|
|
struct UTMMenuBarExtraScene: Scene {
|
|
@ObservedObject var data: UTMData
|
|
@AppStorage("ShowMenuIcon") private var isMenuIconShown: Bool = false
|
|
@AppStorage("HideDockIcon") private var isDockIconHidden: Bool = false
|
|
@Environment(\.openWindow) private var openWindow
|
|
|
|
var body: some Scene {
|
|
MenuBarExtra(isInserted: $isMenuIconShown) {
|
|
Button("Show UTM") {
|
|
openWindow(id: "home")
|
|
}.keyboardShortcut("0")
|
|
.help("Show the main window.")
|
|
Toggle("Hide dock icon on next launch", isOn: $isDockIconHidden)
|
|
.help("Requires restarting UTM to take affect.")
|
|
Divider()
|
|
if data.virtualMachines.isEmpty {
|
|
Text("No virtual machines found.")
|
|
} else {
|
|
ForEach(data.virtualMachines) { vm in
|
|
VMMenuItem(vm: vm).environmentObject(data)
|
|
}
|
|
}
|
|
Divider()
|
|
Button("Quit") {
|
|
NSApp.terminate(self)
|
|
}.keyboardShortcut("Q")
|
|
.help("Terminate UTM and stop all running VMs.")
|
|
} label: {
|
|
Image("MenuBarExtra")
|
|
}
|
|
}
|
|
}
|
|
|
|
private struct VMMenuItem: View {
|
|
@ObservedObject var vm: VMData
|
|
@EnvironmentObject private var data: UTMData
|
|
|
|
var body: some View {
|
|
Menu(vm.detailsTitleLabel) {
|
|
if vm.isStopped {
|
|
Button("Start") {
|
|
data.run(vm: vm)
|
|
}
|
|
} else if !vm.isBusy {
|
|
Button("Stop") {
|
|
data.stop(vm: vm)
|
|
}
|
|
Button("Suspend") {
|
|
let isSnapshot = vm.wrapped?.isRunningAsDisposible ?? false
|
|
vm.wrapped!.requestVmPause(save: !isSnapshot)
|
|
}
|
|
Button("Reset") {
|
|
vm.wrapped!.requestVmReset()
|
|
}
|
|
} else {
|
|
Text("Busy…")
|
|
}
|
|
}
|
|
}
|
|
}
|