Files
luma/Sources/LumaCore/VirtualMachines/VirtualMachineDisplay.swift
Ole André Vadla Ravnås 0e933206fb Boot virtual machines from the target picker
Add a machine abstraction to LumaCore, with QEMU as its first
backend: templates mirroring frida-core's test guests, a QMP client,
and a display that maps QEMU's surface over D-Bus rather than
copying frames.

Leave readiness to the user: drive the guest, mark it ready, and come
back to that snapshot later. Keep the machine in the project, so
booting it again resumes where it was marked. Hand the stub to
addBareboneDevice, so it joins the picker beside the other devices.

Show the display while booting, and afterwards in a Machines panel,
which the side panel now models as an enum to share its slot.
2026-08-20 14:52:52 +02:00

109 lines
2.9 KiB
Swift

import Foundation
#if canImport(Virtualization)
import Virtualization
#endif
@MainActor
public enum VirtualMachineDisplay {
case frames(any VirtualMachineFrameSource)
case hostedWindow(any VirtualMachineWindowSource)
#if canImport(Virtualization)
/// A guest the framework draws itself, given a view to draw into.
@available(macOS 13, *)
case virtualizationGuest(VZVirtualMachine)
#endif
}
@MainActor
public protocol VirtualMachineFrameSource: AnyObject {
var frame: VirtualMachineFrame? { get }
var revision: UInt64 { get }
var pointerIsAbsolute: Bool { get }
func send(_ event: VirtualMachineInputEvent)
}
/// A display that belongs to a window of somebody else's, which the host
/// places where it wants the guest to appear.
@MainActor
public protocol VirtualMachineWindowSource: AnyObject {
var pixelWidth: Int { get }
var pixelHeight: Int { get }
func place(in rect: VirtualMachineScreenRect)
func hide()
}
/// Screen coordinates, measured from the bottom left as the window server
/// counts them.
public struct VirtualMachineScreenRect: Sendable, Equatable {
public let x: Double
public let y: Double
public let width: Double
public let height: Double
public init(x: Double, y: Double, width: Double, height: Double) {
self.x = x
self.y = y
self.width = width
self.height = height
}
}
public struct VirtualMachineFrame: @unchecked Sendable {
public let width: Int
public let height: Int
public let stride: Int
public let format: VirtualMachineFrameFormat
private let pixels: UnsafeRawPointer
private let length: Int
private let owner: any VirtualMachineFrameStorage
public init(
width: Int,
height: Int,
stride: Int,
format: VirtualMachineFrameFormat,
pixels: UnsafeRawPointer,
length: Int,
owner: any VirtualMachineFrameStorage
) {
self.width = width
self.height = height
self.stride = stride
self.format = format
self.pixels = pixels
self.length = length
self.owner = owner
}
public func withPixels<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try body(UnsafeRawBufferPointer(start: pixels, count: length))
}
}
public protocol VirtualMachineFrameStorage: AnyObject, Sendable {
}
public enum VirtualMachineFrameFormat: Sendable {
case bgrx8888
case bgra8888
}
public enum VirtualMachineInputEvent: Sendable, Equatable {
case keyDown(code: UInt32)
case keyUp(code: UInt32)
case pointerMoved(x: Double, y: Double)
case pointerMovedBy(dx: Double, dy: Double)
case pointerButtonDown(button: VirtualMachinePointerButton)
case pointerButtonUp(button: VirtualMachinePointerButton)
case scrolled(deltaX: Double, deltaY: Double)
}
public enum VirtualMachinePointerButton: Sendable, Equatable {
case left
case middle
case right
}