Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions Intents/UTMActionIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,13 @@
//

import AppIntents
#if os(macOS)
import AppKit
#endif

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMStatusActionIntent: UTMIntent {
static var id: String = "UTMStatusActionIntent"
static let title: LocalizedStringResource = "Get Virtual Machine Status"
static let description = IntentDescription("Get the status of a virtual machine.")
static var parameterSummary: some ParameterSummary {
Expand All @@ -38,6 +42,7 @@ struct UTMStatusActionIntent: UTMIntent {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMStartActionIntent: UTMIntent {
static var id: String = "UTMStartActionIntent"
static let title: LocalizedStringResource = "Start Virtual Machine"
static let description = IntentDescription("Start a virtual machine.")
static var parameterSummary: some ParameterSummary {
Expand Down Expand Up @@ -78,7 +83,12 @@ struct UTMStartActionIntent: UTMIntent {
}
options.insert(.bootDisposibleMode)
}
#if os(macOS)
// Ensure the app comes to the foreground before presenting VM UI
NSApp.activate(ignoringOtherApps: true)
#endif
data.run(vm: boxed, options: options)
// For platforms that support foreground continuation, request it (no-op on older SDKs).
if !vm.isHeadless {
if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *), systemContext.currentMode.canContinueInForeground {
try await continueInForeground(alwaysConfirm: false)
Expand All @@ -90,6 +100,7 @@ struct UTMStartActionIntent: UTMIntent {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMStopActionIntent: UTMIntent {
static var id: String = "UTMStopActionIntent"
static let title: LocalizedStringResource = "Stop Virtual Machine"
static let description = IntentDescription("Stop a virtual machine.")
static var parameterSummary: some ParameterSummary {
Expand Down Expand Up @@ -128,6 +139,7 @@ extension UTMVirtualMachineStopMethod: AppEnum {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMPauseActionIntent: UTMIntent {
static var id: String = "UTMPauseActionIntent"
static let title: LocalizedStringResource = "Pause Virtual Machine"
static let description = IntentDescription("Pause a virtual machine.")
static var parameterSummary: some ParameterSummary {
Expand Down Expand Up @@ -157,6 +169,7 @@ struct UTMPauseActionIntent: UTMIntent {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMResumeActionIntent: UTMIntent {
static var id: String = "UTMResumeActionIntent"
static let title: LocalizedStringResource = "Resume Virtual Machine"
static let description = IntentDescription("Resume a virtual machine.")
static var parameterSummary: some ParameterSummary {
Expand All @@ -172,7 +185,7 @@ struct UTMResumeActionIntent: UTMIntent {
@MainActor
func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
try await vm.resume()
if vm.isHeadless {
if !vm.isHeadless {
if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *), systemContext.currentMode.canContinueInForeground {
try await continueInForeground(alwaysConfirm: false)
}
Expand All @@ -183,6 +196,7 @@ struct UTMResumeActionIntent: UTMIntent {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMRestartActionIntent: UTMIntent {
static var id: String = "UTMRestartActionIntent"
static let title: LocalizedStringResource = "Restart Virtual Machine"
static let description = IntentDescription("Restart a virtual machine.")
static var parameterSummary: some ParameterSummary {
Expand All @@ -198,7 +212,7 @@ struct UTMRestartActionIntent: UTMIntent {
@MainActor
func perform(with vm: any UTMVirtualMachine, boxed: VMData) async throws -> some IntentResult {
try await vm.restart()
if vm.isHeadless {
if !vm.isHeadless {
if #available(iOS 26, macOS 26, tvOS 26, watchOS 26, visionOS 26, *), systemContext.currentMode.canContinueInForeground {
try await continueInForeground(alwaysConfirm: false)
}
Expand Down
3 changes: 3 additions & 0 deletions Intents/UTMInputIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ private let kDelayNs: UInt64 = 20000000

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMSendScanCodeIntent: UTMIntent {
static var id: String = "UTMSendScanCodeIntent"
static let title: LocalizedStringResource = "Send Scan Code"
static let description = IntentDescription("Send a sequence of raw keyboard scan codes to the virtual machine. Only supported on QEMU backend.")
static var parameterSummary: some ParameterSummary {
Expand Down Expand Up @@ -63,6 +64,7 @@ struct UTMSendScanCodeIntent: UTMIntent {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMSendKeystrokesIntent: UTMIntent {
static var id: String = "UTMSendKeystrokesIntent"
static let title: LocalizedStringResource = "Send Keystrokes"
static let description = IntentDescription("Send text as a sequence of keystrokes to the virtual machine. Only supported on QEMU backend.")
static var parameterSummary: some ParameterSummary {
Expand Down Expand Up @@ -154,6 +156,7 @@ struct UTMSendKeystrokesIntent: UTMIntent {

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
struct UTMMouseClickIntent: UTMIntent {
static var id: String = "UTMMouseClickIntent"
static let title: LocalizedStringResource = "Send Mouse Click"
static let description = IntentDescription("Send a mouse position and click to the virtual machine. Only supported on QEMU backend.")
static var parameterSummary: some ParameterSummary {
Expand Down
5 changes: 4 additions & 1 deletion Intents/UTMIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ extension UTMIntent {
extension UTMIntent {
@MainActor
func perform() async throws -> T {
guard let vm = data.virtualMachines.first(where: { $0.id == vmEntity.id }), vm.isLoaded else {
guard let vm = data.virtualMachines.first(where: { $0.id == vmEntity.id }) else {
throw UTMIntentError.virtualMachineNotFound
}
if !vm.isLoaded {
do { try vm.load() } catch { throw UTMIntentError.localizedError(error) }
}
do {
return try await perform(with: vm.wrapped!, boxed: vm)
} catch {
Expand Down
4 changes: 0 additions & 4 deletions Intents/UTMVirtualMachineEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ struct UTMVirtualMachineEntity: AppEntity {
}
}

@available(iOS 18, macOS 15, *)
extension UTMVirtualMachineEntity: IndexedEntity {

}

@available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
extension UTMVirtualMachineState: AppEnum {
Expand Down
5 changes: 2 additions & 3 deletions Intents/UTMVirtualMachineEntityQuery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct UTMVirtualMachineEntityQuery: EntityQuery, EntityStringQuery {
await MainActor.run {
data
.virtualMachines
.filter({ $0.isLoaded && identifiers.contains($0.id) })
.filter({ identifiers.contains($0.id) })
.map({ UTMVirtualMachineEntity(from: $0) })
}
}
Expand All @@ -34,7 +34,7 @@ struct UTMVirtualMachineEntityQuery: EntityQuery, EntityStringQuery {
await MainActor.run {
data
.virtualMachines
.filter({ $0.isLoaded && $0.detailsTitleLabel.localizedCaseInsensitiveContains(matching) })
.filter({ $0.detailsTitleLabel.localizedCaseInsensitiveContains(matching) })
.map({ UTMVirtualMachineEntity(from: $0) })
}
}
Expand All @@ -43,7 +43,6 @@ struct UTMVirtualMachineEntityQuery: EntityQuery, EntityStringQuery {
await MainActor.run {
data
.virtualMachines
.filter({ $0.isLoaded })
.map({ UTMVirtualMachineEntity(from: $0) })
}
}
Expand Down
3 changes: 2 additions & 1 deletion Platform/macOS/UTMApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ struct UTMApp: App {
if let message = notification.userInfo?["Message"] as? String {
data.showErrorAlert(message: message)
}
}
}
}

@SceneBuilder
Expand Down Expand Up @@ -81,4 +81,5 @@ struct UTMApp: App {
return oldBody
}
}

}