pub mod capture {
use alloc::sync::Arc;
use alloc::vec::Vec;
use crate::core::error::Result;
use crate::core::props::Props;
use crate::core::sync::{Global, LockRank};
use crate::dev::pc::video::{CLASS_NAME, Video, VideoScanout};
use crate::machine::realize::Instance;
use crate::machine::{Bindings, BuildOptions};
static CONSTRUCTED: Global<Vec<Arc<Video>>> = Global::with_rank(LockRank::LEAF, Vec::new());
fn construct(props: &Props) -> Result<Arc<dyn Instance>> {
let video = Arc::new(Video::new(props)?);
CONSTRUCTED.lock().push(Arc::clone(&video));
Ok(video)
}
pub fn intercept(bindings: &Bindings) -> Result<Bindings> {
let mut out = Bindings::new();
let mut replaced = false;
let classes: Vec<&'static str> = bindings.classes().collect();
for class in classes {
if class == CLASS_NAME {
out.bind(class, construct)?;
replaced = true;
} else if let Some(ctor) = bindings.get(class) {
out.bind(class, ctor)?;
}
}
if !replaced {
out.bind(CLASS_NAME, construct)?;
}
Ok(out)
}
pub fn install(options: &mut BuildOptions) -> Result<()> {
options.bindings = intercept(&options.bindings)?;
Ok(())
}
#[must_use]
pub fn take() -> Option<VideoScanout> {
let mut table = CONSTRUCTED.lock();
let last = table.pop();
table.clear();
last.map(|video| video.scanout())
}
pub fn clear() {
CONSTRUCTED.lock().clear();
}
}
#[cfg(test)]
mod tests {
use super::capture;
use crate::host::display::{Scanout, Surface};
#[test]
fn an_intercepted_binding_hands_the_host_a_picture() {
capture::clear();
assert!(capture::take().is_none(), "nothing has been built");
let mut options = crate::machine::BuildOptions::new()
.with_classes(crate::machine::catalog::classes())
.with_bindings(crate::machine::catalog::bindings().expect("this build's bindings"));
capture::install(&mut options).expect("the class is bound once");
let registry = crate::machine::catalog::registry().expect("this build's registry");
let machine = crate::machine::build(
"one-adapter.machine",
r#"
machine "one-adapter" {
osc dot = 28322000 Hz
space port { width = 16, unassigned = open-bus }
object vga "pc.video" { clock = dot / 9 }
map port 0x03d4 size 0x0002 = vga.crtc-colour
}
"#,
®istry,
&options,
)
.expect("a machine with nothing but a display");
assert_eq!(machine.name(), "one-adapter");
let scanout = capture::take().expect("the constructor kept a handle");
let mut surface = Surface::for_scanout(&scanout);
scanout.capture(&mut surface);
let info = scanout.info();
assert_eq!(surface.width(), info.width);
assert_eq!(surface.height(), info.height);
assert_eq!(info.width, 720, "80 columns of nine pixels");
capture::clear();
}
}