use std::error; use std::io::Read; use std::path; use std::process; use clap::Parser; use piston::EventLoop; use piston::EventSettings; use piston::Events; use piston::RenderEvent; use piston_window as pw; use piston_window::PistonWindow; use piston_window::WindowSettings; #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] struct Cli { /// Executable to run executable: path::PathBuf, /// Number of pixels in a row cols: usize, /// Number of rows in a frame rows: usize, /// Free-format argument to pass to the executable arg: String, /// Frame rate for displaying #[arg(short, long, default_value_t = 25)] frame_rate: u64, /// Turn debugging information on #[arg(short, long, action = clap::ArgAction::Count)] debug: u8, } fn main() -> Result<(), Box> { let cli = Cli::parse(); if cli.debug >= 2 { eprintln!("CLI: {:?}", cli); } let mut child = process::Command::new(&cli.executable) .arg(cli.cols.to_string()) .arg(cli.rows.to_string()) .arg(cli.arg) .stdout(process::Stdio::piped()) .spawn()?; if cli.debug >= 1 { eprintln!( "Running {} as a child process with pid {}", cli.executable.display(), child.id() ); } let mut stdout = child .stdout .take() .ok_or("opening child process stdout failed")?; // TODO support also RGBW let colors = 3; // TODO check for overflow and excessive size let buffer_size = cli.cols * cli.rows * colors; let mut buffer = vec![0_u8; buffer_size]; let mut frame_count = 0; let mut window: PistonWindow = WindowSettings::new( format!("pixelfoo-viewer {}", cli.executable.display()), [1600, 800], ) .exit_on_esc(true) .build()?; let mut events = Events::new(EventSettings::new().ups(cli.frame_rate)); let x_size = cli.cols; let y_size = cli.rows; while let Some(event) = events.next(&mut window) { if let Some(args) = event.render_args() { window.draw_2d(&event, |context, graphics, _device| { // let [vsx, vsy] = context.get_view_size(); let [vsx, vsy] = args.viewport().window_size; // Compute cell size let csx = vsx / (x_size as f64); let csy = vsy / (y_size as f64); let cs = csx.min(csy); pw::clear([0.0, 0.0, 0.0, 1.0], graphics); stdout.read_exact(&mut buffer).expect("pipe read failed"); if cli.debug >= 3 { eprintln!("Received frame {}", frame_count); } for y in 0..y_size { for x in 0..x_size { let i = colors * (y * x_size + x); let r = f32::from(buffer[i + 0]) / 255.0; let g = f32::from(buffer[i + 1]) / 255.0; let b = f32::from(buffer[i + 2]) / 255.0; let color = [r, g, b, 1.0]; let rectangle = [(x as f64) * cs, (y as f64) * cs, cs, cs]; pw::rectangle(color, rectangle, context.transform, graphics); } } }); } frame_count += 1; } child.kill()?; Ok(()) }