use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec;
use alloc::vec::Vec;
use super::{PixelFormat, Scanout as HostScanout, Surface, SurfaceInfo};
use crate::dev::lcd::scanout::Scanout;
#[derive(Debug, Clone)]
pub struct LcdScanout {
engine: Arc<Scanout>,
}
impl LcdScanout {
#[must_use]
pub fn new(engine: Arc<Scanout>) -> LcdScanout {
LcdScanout { engine }
}
#[must_use]
pub fn engine(&self) -> &Arc<Scanout> {
&self.engine
}
}
impl HostScanout for LcdScanout {
fn info(&self) -> SurfaceInfo {
let (width, height) = self.engine.geometry();
SurfaceInfo::new(width, height, PixelFormat::RGB888)
}
fn frame_counter(&self) -> u64 {
self.engine.frame()
}
fn frame_period_ns(&self) -> u64 {
self.engine.frame_period_nanos()
}
fn capture(&self, dst: &mut Surface) -> u64 {
let info = self.info();
dst.reshape(dst.format(), info.width, info.height);
let serial = self.engine.frame();
let mut row = vec![[0u8; 3]; info.width as usize];
for y in 0..info.height {
self.engine.read_row(y, &mut row);
for (x, pixel) in row.iter().enumerate() {
dst.put(x as u32, y, *pixel);
}
}
dst.set_serial(serial);
serial
}
}
pub mod capture {
use super::{Arc, LcdScanout, Scanout, String, Vec};
use crate::core::error::Result;
use crate::core::props::Props;
use crate::core::sync::{Global, LockRank};
use crate::dev::lcd::scanout::{SCANOUT_CLASS, set_frame_rate};
use crate::machine::realize::Instance;
use crate::machine::{Bindings, BuildOptions, Machine};
static CONSTRUCTED: Global<Vec<Arc<Scanout>>> = Global::with_rank(LockRank::LEAF, Vec::new());
fn construct(props: &Props) -> Result<Arc<dyn Instance>> {
let engine = Arc::new(Scanout::new(props)?);
CONSTRUCTED.lock().push(Arc::clone(&engine));
Ok(engine)
}
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 == SCANOUT_CLASS.name {
out.bind(class, construct)?;
replaced = true;
} else if let Some(ctor) = bindings.get(class) {
out.bind(class, ctor)?;
}
}
if !replaced {
out.bind(SCANOUT_CLASS.name, construct)?;
}
Ok(out)
}
pub fn install(options: &mut BuildOptions) -> Result<()> {
options.bindings = intercept(&options.bindings)?;
Ok(())
}
#[must_use]
pub fn take(machine: &Machine) -> Option<LcdScanout> {
let engine = {
let mut table = CONSTRUCTED.lock();
let last = table.pop();
table.clear();
last?
};
resolve_rate(machine, &engine);
Some(LcdScanout::new(engine))
}
fn resolve_rate(machine: &Machine, engine: &Arc<Scanout>) {
let Some(entry) = machine
.devices()
.iter()
.rev()
.find(|d| d.class().name == SCANOUT_CLASS.name)
else {
return;
};
let Some(domain) = entry.domain() else {
return;
};
if let Ok(freq) = machine.clocks().domain_frequency(domain) {
set_frame_rate(engine, freq.num(), freq.den());
}
}
pub fn clear() {
CONSTRUCTED.lock().clear();
}
#[must_use]
pub fn class_name() -> String {
String::from(SCANOUT_CLASS.name)
}
}