use std::sync::Arc;
use objc2::rc::Retained;
use objc2::{DefinedClass, define_class, AllocAnyThread, msg_send};
use objc2_foundation::{NSError, NSObject, NSObjectProtocol};
use objc2_virtualization::{VZVirtualMachine, VZVirtualMachineDelegate};
use tokio::sync::watch;
use crate::vm::VmState;
pub(crate) struct DelegateIvars {
state_tx: Arc<watch::Sender<VmState>>,
}
define_class!(
#[unsafe(super(NSObject))]
#[name = "KasouVmDelegate"]
#[ivars = DelegateIvars]
pub(crate) struct VmDelegate;
unsafe impl NSObjectProtocol for VmDelegate {}
unsafe impl VZVirtualMachineDelegate for VmDelegate {
#[unsafe(method(guestDidStopVirtualMachine:))]
fn guest_did_stop(&self, _vm: &VZVirtualMachine) {
tracing::info!("guest initiated VM stop");
let _ = self.ivars().state_tx.send(VmState::Stopped);
}
#[unsafe(method(virtualMachine:didStopWithError:))]
fn did_stop_with_error(&self, _vm: &VZVirtualMachine, error: &NSError) {
let chain = crate::util::ns_error_chain(error);
tracing::error!(error = %chain, "VM stopped with error");
let _ = self.ivars().state_tx.send(VmState::Error);
}
}
);
impl VmDelegate {
pub(crate) fn new(state_tx: Arc<watch::Sender<VmState>>) -> Retained<Self> {
let this = Self::alloc().set_ivars(DelegateIvars { state_tx });
unsafe { msg_send![super(this), init] }
}
}