use alloc::string::String;
use alloc::sync::Arc;
use alloc::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};
use crate::core::error::Result;
use crate::core::hosts::{Captured, HostKind, HostObjects};
use crate::dev::lcd::scanout::{SCANOUT_CLASS, set_frame_rate};
use crate::machine::{BuildOptions, Machine};
pub fn install(options: &mut BuildOptions) -> Result<()> {
let seen: Arc<Captured<Scanout>> =
options
.realize
.hosts
.open(HostKind::CAPTURE, SCANOUT_CLASS.name, Captured::new)?;
options.bindings.replace(SCANOUT_CLASS.name, move |props| {
let engine = Arc::new(Scanout::new(props)?);
seen.push(&engine);
Ok(engine)
});
Ok(())
}
#[must_use]
pub fn take(hosts: &HostObjects, machine: &Machine) -> Option<LcdScanout> {
let seen = hosts
.get::<Captured<Scanout>>(HostKind::CAPTURE, SCANOUT_CLASS.name)
.ok()
.flatten()?;
let engine = seen.take()?;
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());
}
}
#[must_use]
pub fn class_name() -> String {
String::from(SCANOUT_CLASS.name)
}
}