vzense-rust 0.4.2

High-level library for Vzense cameras
Documentation
use pixels::{Pixels, SurfaceTexture};
use std::{
    fmt,
    sync::{
        Arc,
        mpsc::{Receiver, SendError, Sender, channel},
    },
    thread::JoinHandle,
};
use vzense_rust::Resolution;
use winit::{
    application::ApplicationHandler,
    dpi::LogicalSize,
    event::{ElementState, MouseButton, WindowEvent},
    event_loop::{ActiveEventLoop, ControlFlow, EventLoop},
    window::{Window, WindowAttributes, WindowId},
};

pub enum BottomView {
    Color,
    Touch,
    Ir,
}
impl fmt::Display for BottomView {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            BottomView::Color => write!(f, "Color"),
            BottomView::Touch => write!(f, "Touch"),
            BottomView::Ir => write!(f, "IR"),
        }
    }
}

pub enum WindowMessage {
    MouseClick,
    Exit,
}

#[derive(Debug, Clone)]
pub struct VideoBuffer(pub Arc<Vec<u8>>);
impl VideoBuffer {
    fn new(pixel_count: usize) -> Self {
        VideoBuffer(Arc::new(
            // black frame
            std::iter::repeat_n(&[0, 0, 0, 255], pixel_count)
                .flatten()
                .cloned()
                .collect(),
        ))
    }
}

pub struct VideoProducer {
    rx_old: Receiver<VideoBuffer>,
    sx_new: Sender<VideoBuffer>,
    sx_info: Sender<String>,
    pub rx_window_message: Receiver<WindowMessage>,
}
impl VideoProducer {
    pub fn grab_old_buffer(&self) -> Option<VideoBuffer> {
        self.rx_old.try_recv().ok()
    }
    pub fn send(&self, buffer: VideoBuffer) -> Result<(), SendError<String>> {
        if self.sx_new.send(buffer).is_err() {
            Err(SendError("cannot send filled buffer".to_string()))
        } else {
            Ok(())
        }
    }
    pub fn set_info(&self, info: String) {
        self.sx_info.send(info).unwrap();
    }
}

pub struct VideoConsumer {
    pub name: String,
    pub resolution: Resolution,
    pub rx_new: Receiver<VideoBuffer>,
    pub sx_old: Sender<VideoBuffer>,
    pub rx_info: Receiver<String>,
    pub sx_window_message: Sender<WindowMessage>,
}

// set up new buffer pool
const POOL_SIZE: usize = 3;
pub fn new(name: &str, width: u32, height: u32) -> (VideoProducer, VideoConsumer) {
    let (sx_new, rx_new) = channel();
    let (sx_old, rx_old) = channel();
    let (sx_info, rx_info) = channel();
    let (sx_window_message, rx_window_message) = channel();
    for _ in 0..POOL_SIZE {
        sx_old
            .send(VideoBuffer::new((width * height) as usize))
            .unwrap();
    }
    (
        VideoProducer {
            rx_old,
            sx_new,
            sx_info,
            rx_window_message,
        },
        VideoConsumer {
            name: name.to_string(),
            resolution: Resolution::new(width, height),
            rx_new,
            sx_old,
            rx_info,
            sx_window_message,
        },
    )
}

type CameraHandle = Option<JoinHandle<Result<(), String>>>;

struct App<'a> {
    window: Arc<Window>,
    pixels: Pixels<'a>,
    consumer: VideoConsumer,
    camera_handle: CameraHandle,
}

impl ApplicationHandler for App<'_> {
    fn resumed(&mut self, _event_loop: &ActiveEventLoop) {}

    fn window_event(
        &mut self,
        _event_loop: &ActiveEventLoop,
        _window_id: WindowId,
        event: WindowEvent,
    ) {
        match event {
            WindowEvent::CloseRequested => {
                println!("stopping camera thread...");
                self.consumer
                    .sx_window_message
                    .send(WindowMessage::Exit)
                    .ok();
            }
            WindowEvent::MouseInput {
                state: ElementState::Pressed,
                button: MouseButton::Left,
                ..
            } => {
                self.consumer
                    .sx_window_message
                    .send(WindowMessage::MouseClick)
                    .ok();
            }
            _ => (),
        }
    }

    fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
        // check if camera thread finished
        if let Some(handle) = &self.camera_handle {
            if handle.is_finished() {
                if let Some(handle) = self.camera_handle.take() {
                    if let Ok(Err(e)) = handle.join() {
                        eprintln!("camera thread finished: {e}");
                    }
                }
                event_loop.exit();
            }
        }

        // check for new frame
        let mut new_frame = None;

        // draining
        while let Ok(intermediate_frame) = self.consumer.rx_new.try_recv() {
            if let Some(frame) = new_frame {
                self.consumer.sx_old.send(frame).expect("couldn't drain");
            }
            new_frame = Some(intermediate_frame);
        }

        if let Some(frame) = new_frame {
            // set pixels
            self.pixels.frame_mut().copy_from_slice(&frame.0);
            // send used frame back
            self.consumer
                .sx_old
                .send(frame)
                .expect("couldn't send old buffer");

            if let Err(err) = self.pixels.render() {
                eprintln!("pixels render failed {err}");
            }
        }

        if let Some(info) = self.consumer.rx_info.try_iter().last() {
            self.window.set_title(&(self.consumer.name.clone() + &info));
        }
    }
}

pub fn run(
    camera_handle: CameraHandle,
    consumer: VideoConsumer,
) -> Result<(), Box<dyn std::error::Error>> {
    let event_loop = EventLoop::new()?;
    event_loop.set_control_flow(ControlFlow::Poll);

    let (width, height) = consumer.resolution.to_tuple();

    // Create the window and its state immediately (deprecated but much shorter)
    #[allow(deprecated)]
    let window = Arc::new(
        event_loop.create_window(
            WindowAttributes::default()
                .with_title(consumer.name.clone())
                .with_inner_size(LogicalSize::new(width, height))
                .with_resizable(false),
        )?,
    );

    let surface_texture = SurfaceTexture::new(width, height, window.clone());
    let pixels = Pixels::new(width, height, surface_texture)?;

    let mut app = App {
        window,
        pixels,
        consumer,
        camera_handle,
    };

    event_loop.run_app(&mut app)?;

    Ok(())
}